Sommaire

BONJOUR
Musique "Invention BWV784" J.S. BACH 1685-1750

Designation

- Designation -

Nous veillons aux respects des droits d'auteur : si par erreur un contenu protégé est disponible sans indication sur ce site, merci de contacter l'administrateur, il sera retiré ou mis à jour dans les plus brefs délais.


Accueil

J2M

La page vide

Normal, dit comme ça,
vous ne risquez pas de comprendre.


ARTeam Tutorial

http://cracking.accessroot.com

http://forum.accessroot.com
Assembleur Win32 Codage pour Crackeurs

Mots Clés | Coding(codage), ASM, Win32, GUI
Niveau | De débutant à Intermédiaire
Test OS | XP Pro SP2
Auteur Traduit par | Goppit - 17/03/2008 - goppit@hotmail.com SPOKE3FFF & DcSpirou - http://xtx.free.fr/ Site de GoldoCrack TEAM ODC

Outils utilisés:
MASM32 v9.0 | http://www.masm32.com/masmdl.htm
lib2inc v1.11 | inclus
WinAsm Studio Full v5.1.1.0 | http://www.winasm.net/index.php?ind=downloads&op =download_file&ide=119&file=WinAsm511.zip
WinAsm v5.1.3.0 Update* | http://www.winasm.net/forum/index.php?act=Attach&type=post&id=4767
WinAsm help file* | http://www.winasm.net/forum/index.php?act=Attach&type=post&id=381
Win32.hlp file | http://www.winasm.net/index.php?ind=downloads&op =download_file&ide=23&file=win32help.zip
OllyDbg v1.10 | http://www.ollydbg.de/odbg110.zip
RGN Generator v1.01 | http://www.codeguru.com/code/legacy/tools/RGNCreator_demo.zip
Table Extractor v1.34 | inclus
uFMOD | inclus
Extras:
M$ Platform SDK | http://www.Microsoft.com/downloads/details.aspx?FamilyId=A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5&displaylang=en
H2 Viewer v1.3.5.0 | http://helpware.net/downloads/H2Viewer135.zip
WinAsm MSDN h2help add-in* | http://www.winasm.net/forum/index.php?act=Attach&type=post&id=4096
Winspector v1.0b build 35 | http://www.windows-spy.com/files/Winspector_setupU.exe
SignFinder by Asterix | inclus

Notez S’il vous plaît que les liens donnés ci-dessus vous donneront accès à différents outils qui au moment où je rédige ce tutorial les liens sont encore bons , mais plusieurs d'entre eux sont très souvent mis à jour. Donc soyez prêt à chercher sur Google si certains liens sont HS. Le symbole ( * ) Indique que les mises à jour du forum WinAsm exigent que vous soyez enregistré. Je vous ai inclus un fichier qui regroupe les utilitaires Ici sur lesquels je ne trouvais plus d'activité sur leur site respectif.

Introduction

But de ce Tutorial

La plupart (voir même la totalité!) des meilleurs hackeurs, crackeurs et reverseurs sont également des programmeurs. Il est pratiquement impossible de bien suivre et apprendre le RCE, sans un minimum de connaissance en programmation ASM et un grand nombre des plus vieux fichiers txt sur l'apprentissage du Cracking - disaient je cite : "Avant d'aller plus loin apprenez l'Assembleur, et puis revenez lire ceci ". C'est encore plus vrai aujourd'hui que jamais car des exécutables utilisent des packeurs pour être protégé de l'utilisateur et sont de plus en plus complexes avec diverses méthodes d'approche sur l'unpacking, le déboguage et la reconstruction d'IAT. Cela a conduit des groupes de cracking à coder des outils personnalisés juste en se penchant sur ce sujet avec leur débogueur, désassembleur et outils Comme ImpREC. Elle a également conduit à la venue des loaders de plus en plus populaires, c'est le résultat de l'excellent travail en Cracking avec loaders de la série ARTeams. Ils ne sont cependant pas si facile à comprendre pour les débutants sans aucune Connaissances en codage ASM.

J'ai écrit ce tutorial pour combler et le manque de connaissance du débutant et faire un cours sur l'avancée des loaders, traceurs,… de même,que sur applications console(DOS) et comment utiliser et comment fonctionnent ces outils , mon deuxième objectif était d'illustrer sur le fait qu'il peut être facile de créer des applications GUI(fenêtre) en assembleur (ASM).

C'est grâce à l'esprit de partage des renverseurs de toutes les communautés sur la programmation assembleur ,que j'ai pu réutilisés et adaptés leur code d'une grande variété de sources que vous n'oublierez pas de remercier avec une grande gratitude lorsque cela vous sera possible par les "Greetz" habituels que vous mettrez dans vos futures applications codées grâce à leur aide .

Outils Requis

Il y a 4 principaux outils nécessaires:

1. Un assembleur
2. Un IDE ou Environnement de développement intégré
3. Un débogueur
4. Une source d'information concernant la plate-forme (le système d'exploitation -- WinXP pro dans mon cas) et ses Application(s) Programming Interface (L'API Win32)

Le choix de l'assembleur et IDE est d'ordre personnel. Le pack MASM32 mis à disposition spécifiquement pour coder des applications graphiques pour Windows et de plus il y a beaucoup plus de soutien et de code source disponible sur Internet qui utilisent la syntaxe de MASM . Je m'en rends compte fréquemment en recevant des critiques sur les lacunes et la lenteur en rapport à TASM ,et je pense que le code TASM est moins lisible, c'est pour ses raisons ci-dessus, je reste à MASM.

Je suis favorable à l'interface WinAsm Studio IDE parce qu'elle offre d'excellentes fonctionnalités et est très compacte et conçue uniquement pour MASM32 .Et ne présente aucun désagrément résultant de la nécessité de l'appui d'autres assembleurs. La structure des répertoires créés par son Assistant de nouveau projet est également beaucoup plus simple que pour RadASM par exemple. L'annexe 1 (Appendix 1) contient l'installation et les réglages détaillés de MASM32 et WinAsm plus quelques façons de les personnaliser.

Bien que le choix du débogueur soit également personnelle . Je suggère à tous les débutants de commencer avec un débogueur comme Ollydbg Il est très puissant et facile à utiliser par rapport à SoftIce et possède autant de plugins disponibles utiles.

La dernière des exigences qui s'ajoutera à celles ci-dessus sera remplie,soit en possèdent l'ancien fichier d'aide Win32.hlp ou par la mise à jour MSDN des bibliothèques disponibles en ligne dans le cadre de la Plate-forme SDK disponible en téléchargement libre chez M$.

Toutes les Informations que vous pourrez tirer profit dans ce tutorial va se faire en deux catégories principales:

1. MASM32 syntaxe -Comment écrire correctement les instructions en ASM sous MASM
2. L'architecture Windows et ses API(s) - Toutes les bonnes choses pour le reverseur

Nouveaux concepts de ces groupes sont Mis en évidence à la tête de chaque chapitre. Tous les codes Utilisés dans ces didacticiels sont écrit au format texte dans la La section des Sourcecodes. Ce fichier est là pour vous faciliter l'utilisation de WinAsm ou de les coller dans l'éditeur de votre choix. Des captures d'écran de l'éditeur de code WinAsm sont utilisés tout au long des didacticiels d'où le fait de la coloration syntaxique de manière à ce que ce soit plus lisible. Divers autres éléments utilisés dans les projets (Bitmaps, les icônes et les services publics) sont Également inclus dans un fichier zip fourni Ici avec ce tutorial. Une petite remarque finale à propos du code, je suis favorable au codage sous WinXP donc je n'ai fait aucun effort pour assurer la compatibilité avec Win9x. Certaines des routines (par exemple celle de ("alphablending") dans le chapitre keygen) ne fonctionnera pas sur les anciennes versions de windows.

B Compiler un Exécutable

Avant je vais expliquer brièvement Le processus de construction d'un exécutable. L'assembleur comprend en fait un ensemble de plusieurs consoles basées sur des composantes, notamment l'assembleur lui-même, un linker, un compilateur de ressources et autres. En principe vous obtiendrez de l'aide de l' IDE qu'il alimentera automatiquement la correction des syntaxes sur les instructions de commande (qui peuvent être complexes) .Ces éléments vous permettrons de voir les résultats d'un environnement graphique.

Les instructions du langage Assembleur sont écrites et sauvegardées en Simple fichier texte avec l'extension ". Asm". L'assembleur Traduit les instructions de ce code source en Code machine compréhensible par le CPU et convertit les valeurs numériques binaires en décimales. Le produit est un objet Fichier (. Obj), qui contient des instructions de la machine, des données et des informations nécessaires pour placer correctement les instructions dans la Mémoire lorsque l'application est exécutée.

Le Linker combine les fichiers objets et tout Les fichiers de ressources (voir ci-dessous) pour créer le fichier exécutable final. L'éditeur de liens cherche les fichiers objets à trouver toutes les fonctions externes et les liens utilisés dans les bibliothèques d'importation Pour ces fonctions. Le fichier résultant peut désormais être exécuté par Le système d'exploitation. Lorsque cela se produit, il est mappé en mémoire par le Loader de Windows, toutes les DLL dont il dépend sont aussi Chargées, et l'instruction de son entrypoint est appelée.

Ressources

Les ressources sont des données stockées dans l' Exécutable, y compris les icônes, stringtables, menus, Dialogues, des images, des données définis par l'utilisateur, etc Les ressources allouées à un programme sont Définies pour la première fois dans un script de ressources (. Fichier rc). Ceci est un simple fichier texte qui peut être écrit soit manuellement, soit en utilisant des ressources WinAsm's Visual Editor. Les ressources sont alors Compilées par un compilateur de ressources dans un fichier binaire de ressources (. Res) Qui est lié à l'exécutable par le linker. Les ressources sont accessibles par Numéros d'identification uniques qui sont définies dans le script de ressources. En ASM ces codes source de la même ID sont utilisés pour accéder à la ressource.


ARTeam Tutorial

Squelette d'Assemblage de Base d'un Programme

Nouveaux Concepts:

Utilisation de WinAsm et
directives d'Assembleur MASM32
Utilisation d'Invoke avec fonctions d'API Win32

Nouvelles Fonctions d'API:

ExitProcess

Utilisation de WinAsm

La copie d'écran suivante illustre les parties de l'interface WinAsm. Le code source que vous tapez apparaît dans l'Éditeur de fenêtre (à gauche). La fenêtre de l'explorateur (à droite) montre les fichiers qui font partie de l'actuel projet, y compris le code asm, inclure des fichiers, Ressources,… Le boutton "Go All" (indiqué par le pointeur de la souris dans la capture d'écran) va assembler et lier votre code source et puis exécuter le fichier résultant. Les commandes passées à l'assembleur et de linker avec les résultats sont affichés dans la fenêtre de sortie (Inférieur).

Les 7 lignes de code montrées ci-dessus représentent le programme le plus petit qui assemblera en réalité, liera et exécutera. Il consiste en une simple instruction RET à l'entrypoint qui quand exécuté rend immédiatement le contrôle en retour Windows. Pour le faire dans un squelette utile autour du quel on peut construire des programmes réels nous remplaçons le RET d'un appel par ExitProcess - l'API standard pour quitter un appendice.

Pour commencer à coder, ouvrez WinAsm et cliquez sur le bouton à l'extrême gauche sur la barre d'outils. Notez que je fais mettre hors de service les chefs de projet récents au démarrage et j'utilise le Nouvel assistant de Projet Ajoutent - dans lequel il y a une grande amélioration sur la fonctionnalité existante - voir Ici. Si vous réglez votre système comme ici vous devriez voir:

Cliquez Ensuite et Finissez ensuite . La Norme(le Standard) EXE le type de projet est mise en évidence par défaut qui met l'assembleur et les options linker pour produire un Win32 GUI exécutable. WinAsm vous incitera à sauver le nouveau fichier de projet et le nouveau fichier d'ASM, après vous verrez que le rédacteur de code vide l'écran.

Collez maintenant skeleton.asm de la section de Code source de ce fichier dans le rédacteur de code la fenêtre. Le déclic "GO ALL" vous incitera d'abord à sauver le projet et le fichier asm à un emplacement approprié avant la construction et l'exécution de votre fichier. Votre code devrait y ressembler (l'annexe 1 de contrôle) : appendix 1 s'il y a des erreurs ou de ponctuation et de syntaxe seront colorée :

Nous analyserons maintenant ligne par la ligne. La plupart de ce code représente des directives (des instructions à l'assembleur). Il y a seulement une ligne de code de programme réel - l'appel à la fonction d'API ExitProcess.

.386 - Dit à MASM d'utiliser le jeu d'instruction d'Intel 80386.

.model flat, stdcall - Spécifie le modèle de mémoire plat et l'appel standard appelant la convention.

option casemap:none - Spécifie que les labels doivent être sensibles dans ce cas.

include kernel32.inc - Dit à MASM de traiter le fichier nommé (comme si le contenu a été copié dans le code source ici)

includelib kernel32.lib - Dit au linker qui importe des bibliothèques pour se lier avec (kernel32.lib dans ce cas).

.data - Spécifie des données initialisées (les variables que l'on donne à une valeur de départ) va ici.

.data? - Spécifie des données non initialisées ici (les variables qui n'ont pas encore de valeur). L'Espace est alloué quand le programme est chargé dans la mémoire mais la taille de fichier sur le disque n'est pas augmentée.

.const - Spécifie que l'on déclare une constantes ici.

.code - Spécifie que le code exécutable va ici.

start: - L'étiquette arbitraire pour spécifier le début de code (label de note est suivie par ":" quand il est d'abord déclaré).

invoke ExitProcess,0 - L'exécution commence ici, à l'instruction immédiatement au-dessous du label indiquée selon la directive de fin. Il y a seulement une instruction ici et il appelle la fonction d'API ExitProcess qui est la méthode préférée de finir un programme et rendre la main à Windows.

end start - Marque la fin du module et met le programme entrypoint au label indiqué (le label de note n'a pas besoin ":" ici comme on l'a déclaré plus tôt).

Les API Win32

L'API de Windows (Application, Programmant l'Interface) inclut une collection énorme de types de données, constantes, des fonctions et structure utilisé pour créer des applications pour OS Windows. La plupart des API fonctionnent, y compris ExitProcess que nous avons utilisé et dit, sont stockés dans 3 DLLS principales :

KERNEL32.DLL - Niveau bas kernel services
GDI32.DLL - Graphics Device Interface: drawing and printing
USER32.DLL - User Interface controls, windows and messaging services

Si vous utilisez des fonctions d'API de Windows dans votre programme, votre programme doit "importer" les fonctions du DLLS. Les bibliothèques d'importation (.lib des fichiers) contiennent des informations le linker doit résoudre des appels aux fonctions résidant dans les DLLS, donc le système peut charger la DLL indiqué et placer les fonctions exportées nécessaire quand votre code est exécuté. Par exemple, pour appeler la fonction d'ExitProcess qui réside dans kernel32.dll, vous devez lier votre code avec.

Include (.inc) Les fichiers contiennent "les prototypes" qui définissent les attributs de toutes les fonctions stockées dans la DLL du même nom. Celles-ci peuvent automatiquement produire des fichiers de bibliothèque utilisant le lib2inc utiliy. Il n'y a aucune façon facile de produire un fichier de bibliothèque d'un donné DLL, mais heureusement la plupart des fonctions d'API que vous exigerez sont placé(localisé) dans DLLS pour lequel la distribution MASM32 a des fichiers de bibliothèque appropriés.

La documentation d'API officielle Win32 est écrite pour C et C ++ des programmeurs et une fonction d'API typique est définie dans le format suivant :

Retour Type FonctionName ( ParamType1 ParamName1, ParamType2 ParamName2,...);

Utilisation de la fonction SetWindowText comme un exemple de la Référence du Programmeur Win32 :

La syntaxe C est facilement convertie en assembleur. Le mot BOOL est le type de données que la fonction son résultat et rendre comme on s'attend peut être méconnu. HWND est répété une fois représenter la taille et le type de données fournies à la fonction (vraiment juste un DWORD ici - ces types de données de Windows sont tous définis dans le fichier de windows.inc dans C:\masm32\include) et la seconde comme le nom des données (ici étant courts pour le module de la fenêtre). C'est vraiment

PUSH lpString
PUSH hWnd
CALL SetWindowText

Notez que dans l'assembleur les paramètres sont Pushés sur la pile dans l'ordre inverse comparée à la définition C de Win32.hlp, de droite à gauche. C'est le STDCALL la convention appelante qui est utilisée presque exclusivement dans des Fenêtres ASM.

Maintenant vous savez comment utiliser des définitions d'API de Win32.hlp, vous pouvez le consulter pour une explication de n'importe quelle API ça fonctionne nous l'utiliserons pendant ce tutorial..

Utilisation d'Invoke

L'inclusion de prototypes dans votre code source a 2 avantages dans MASM. Premièrement l'instruction INVOKE peut être utilisée au lieu du traditionnel PUSH/CALL . INVOKE est plus compact et les paramètres ne doivent pas être disposés dans l'ordre inverse :

INVOKE SetWindowText,hWnd,lpString

Le deuxième avantage d'INVOKE est qu'il permet la vérification de type donc le linker peut garnir de flag une erreur si on passe des paramètres incorrects à la fonction dans votre code. Des prototypes de fonction individuels peuvent être ajoutés au code manuellement au lieu de l'inclusion de .inc entier classent, mais doit arriver plus tôt dans le code que l'instruction INVOKE.

L'utilisation de prototypes et INVOKE peut être appliqué aux fonctions que vous écrirez vous-même comme nous le verrons par la suite.


ARTeam Tutorial

Programme Simple pour une boîte de message "MessageBox"

Nouveaux Concepts:

Variables et constantes
Utilisation d'Ollydbg pour désassembler et debogger votre code

Nouvelle fonction API:

MessageBox

Traditionnellement, les tutorials pour la programmation commence Avec une application qui affiche le message "Hello World "dans une console. Les fenêtres d'interface graphique équivalent à ça est la fonction MessageBox.Dans WinAsm pour commencer, ouvrez un nouveau Projet exe et coller dans le code messagebox.asm:

Cliquez sur "Go All", save project et ASM Fichiers, puis vous devriez voir ceci:

Maintenant nous allons analyser les nouvelles parties.

Header & Library Files

windows.inc Est un fichier maître qui définit inclure toutes les données Windows Types, les appels de fonctions, de structures de données et constante Identifiants pour vous permettre de les citer par leur nom dans votre code. A noter qu'ici, c'est windows.inc définit NULL et si MB_OK Que celles-ci peuvent être utilisés par leur nom pour rendre le code plus Lisible.

user32.inc et user32.lib sont requies pour invoker MessageBox.

Variables

Nous avons définis 2 Zéro fin de texte string qui appel MsgBoxCaption et MsgBoxText. Ceux-ci seront Affichée par la message box.

Variables et constantes sont tous déclarés De la même manière à partir de l'identifiant (name), la taille et aussi la valeur. Comme les chaînes varient en taille, ils sont définis comme un Octet zéro. Voici quelques exemples:

Program Code

La nouvelles instruction invoke la fonction MessageBox Qui prend 4 paramètres comme nous le verrons dans Win32.hlp:

Notre code passe ces paramètres comme Suit:

NULL - Il n'ya pas de fenêtre parente.
addr MsgBoxText - L'adresse de notre texte string.
addr MsgBoxCaption - L'adresse de notre caption.
MB_OK - Partie d'un ensemble de styles prédéfinis.

L' addr operateur Est utilisé pour transmettre des L'adresse d'un label d'identification ou à une fonction. Voir Ici pour la syntaxe.

NOTE EN OUTRE

Les messages eux-même peuvent être répartis dans Plusieurs lignes en utilisant le retour de char (13h) et d'une ligne vierge (10h) les codes à insérer une nouvelle ligne. Le code suivant:

Produira ceci:

Le dernier paramètre passé aux contrôles MessageBox en matière de style (entre autres choses), il a les boutons:

Et si elle affiche un icon:

Et le bouton qui est sélectionné par Default:

Les constantes qui les contrôlent sont Windows.inc défini dans le fichier qui peut aussi être ouvert dans WinAsm Comme le montre cette image:

Une sélection de ces derniers sont également définis dans C:\Program Files\WinAsm Studio\API\MasmApiConst.vaa Qui Les rend disponibles à partir de WinAsm auto complete menu popup. Utilisez les touches fléchées pour sélectionner et entrer à partir de la liste déroulante. Plusieurs constantes peuvent être combinés en un paramètre pour une Fonction en les séparant avec "or" or "+". Par exemple Modifier le code en:

invoke MessageBox, 0, addr MsgBoxText, addr MsgBoxCaption, MB_YESNO + MB_ICONASTERISK + MB_DEFBUTTON2

will produce this:

Nous pouvons voir à windows.inc que certains des Identificateurs sont des doublons et aussi quelles caractéristiques Messagebox par défaut utilisé par le système est composé de (Ce qui égale zéro). Paramètres et constantes pour les autres fonctions API sont appliquées de la même manière.

Si nous ouvrir nos messagebox programme (la Version originale) dans OllyDbg nous voyons un désassemblage du Code qui a été placé dans notre exécutable:

C'est une bonne habitude à prendre pour s'aider Découvrez le débogage des compétences et de voir en réalité ce que produit comme code MASM . L'invoque des instructions qui sont utilisés par l' Assembleur existent purement et pour la commodité du Programmeur ont été remplacés par la Séquence traditionnel PUSH / CALL . Les lignes en jaune représentent un saut thunk table Contenant des pointeurs vers les adresses d'importation pour le Tableau 2 Appels API que nous avons pris et qui sont ajoutés par le linker.

Olly permet également de charger et d'afficher notre code source le synchronisé avec le désassemblage. Pour les besoins de masm Et produire une version debug de nos app. Cela peut être Obtenus avec WinAsm. Par le "Make" menu select "Set Active Build" et choisi "Debug Version". Maintenant écrit"Go All" À nouveau et vous verrez certains des fichiers supplémentaires dans le cadre du dossier projet avec. Acabit et. Pdb extensions.

Cette fois, si nous chargeons nos exécutable dans Olly, Nous aurons un peu plus d'informations dans la Fenêtre désassemblage (tels que les noms de nos variables), mais aussi si nous Cliquons sur le lien "commentaire" en-tête de la barre des taches (voir curseur de la souris Dans la capture d'écran ci-dessous), il affiche le code source Correspondant aux instructions de désassemblage dans la colonne.

En outre, si nous sélectionnons"Source files" par le "View" menu dans Olly et doublecliquons sur notre fichier ASM puis L'ensemble du code source s'affichera dans une nouvelle fenêtre. Cela peut Être faits pour rester à la page via le menu droit et montrera L'instruction en cours d'exécution mis en surbrillance par une barre noire:

Cela peut rendre vos propres applications de débogage rapide Et plus facile mais rappelez-vous que le reverse est basé Sur la modification des exécutables sans le code source et en Général, vous serez très heureux si vous lancez une application de débogage Et qu'il s'agit d'une version debug plutôt que d'une version libre .


ARTeam Tutorial

Aperçu de l'architecture des fenêtres

Nouvelle Conception:

Types de fenêtres
Messages de la fenêtre
Procedures Windows
Windows Class
Le Message Queue & Message Loop
Boites de Dialogues & Controles fenêtre enfant
Handles

Cette section est nécessaire avant de passer aux autres, afin de comprendre la conception des fenêtres et des boites de dialogues.

T ypes de fenêtres

En programmation les fenêtres sont désignées comme des objets.Il existe diverses types de fenêtre telle que la fenêtre principale de demande qui peut contenir une barre de titre qui affiche le nom du programme, Un menu, et parfois une barre d'outils et barre de défilement. D'autres types De fenêtres sont les DialogBox, qui peuvent ou non avoir une Barre de titre,les MessageBox sont très basique.Boutons poussoirs, radio buttons, check boxes, list boxes, scroll bars, progress bars, et entrée de texte dans des champs prévue Font partie d' une fenêtre principale sont aussi appelées types de fenêtres "fenêtres enfants." Nous allons parler des boîtes de dialogue et des contrôles de fenêtres enfants de manière plus approfondie ci-dessous.

Il y a 2 areas(endroits) importants pour une fenêtre standard . Le client area est la partie principale de la fenêtre Où se produit l'affichage de l'application , tels que du texte ou Graphiques.La barre titre,barre menu,menu de la fenêtre,boutons minimise et agrandir,bordures, et barres de défilements Sont appelées collectivement pour la fenêtre nonclient area. Le système gère la plupart des aspects de l'endroit nonclient et de gérer l'apparence et le comportement de ses client area:

Fenêtre Messages

La fenêtre reçois la saisie de l'utilisateur, tels que l'interaction du Clavier ou de la souris sous forme de "fenêtres Messages "à partir du système d'exploitation.D'autres fenêtres utilisent aussi des Messages qui servent à communiquer avec d'autres fenêtres. Par exemple, Lorsque l' utilisateur déplace la bordure de la fenêtre d'une application pour la redimensionner, l' OS gère tout le code pour permettre à la taille de la fenêtre d'être changée, mais le soft va réagir en modifiant son Affichage pour remplir la nouvelle fenêtre. Comment savoir quand la fenêtre d'une app a été redimensionnée? L'OS envoie pour le programme une fenêtre avec un message du changement.Voilà un autre exemple comment savoir si le clic d'un bouton poussoir d'une fenêtre est géré par cette fenêtre. Dans la plupart des Programmes Windows, la majeure partie du programme est consacrée à La manipulation des messages. Un message est constitué d'un MessageID commençant par Les lettres indiquant le type du message (par exemple WM_) et Deux paramètres (lParam et wParam) qui (si nécessaire) Contiennent des informations supplémentaires.

Procedure Windows

L'OS renvoi des messages à un programme de Appelant une fonction spéciale (La "procédure Windows" ou WndProcCela permet de conserver l'intérieur et de le transmettre le message en tant que paramètre.Les Fonctions écrites par le programmeur qui appelle l'OS de cette façon sont appelés "procedures callback". Chaque fenêtre que créé un programme est associée à une Procédure Windows qui traite les messages de l'OS et d'autres fenêtres. Toute l'action réelle se produit par la procédure Windows -- Presque tous les programmes Windows gèrent Un message par procédure Windows. La procédure Windows détermine ce qu'affiche la fenêtre dans son espace client et Comment elle réagit à la saisie de texte par l'utilisateur.Cette fenêtre s'appelle WndProc Quand une fenêtre est créée et détruite. Votre Procédure Windows permet de recevoir des messages Dont il n'a pas besoin de le traiter il le fait lui-même et dans ce cas vous Devez avoir un message par défaut de Windows handler DefWindowProc pour la transformation.

Windows Class

Une création de fenêtre est toujours basée sur une"Windows Class".La Windows Class Définit la Procédure qui traite les messages de la fenêtre. L'utilisation d' une Windows Class permet à plusieurs fenêtres d'être créé en fonction de la Même classe, et donc d'utiliser la même procédure Windows. Par exemple, toutes les fenêtres, y compris les boutons poussoirs, Des cases à cocher, boutons radio, sont créées basés sur la même Windows Class Cette Windows Class est associée à une Procédure Windows située dans une DLL système qui traite les messages de sur tous les boutons Windows. Avant de créer une application Windows, vous devez enregistrer une Windows Class en appelant la fonction RegisterClass. La Windows Class définit également les grandes Caractéristiques d'une fenêtre comprenant des icônes et couleurs en arrière-plan .

Lorsque vous créez une fenêtre en appelant la fonction CreateWindow , vous spécifiez plus en détails L'information sur l'affichage de la fenêtre. Par exemple Nous savons que tous les boutons fonctionnent de la même façon car ils utilisent tous WndProc , mais certains boutons n'ont pas le même aspect. Ils peuvent avoir différentes tailles, différents endroits sur l' Écran, et différentes chaînes de texte. Ces caractéristiques Font partie de la définition Windows plutôt que de la Class Windows.

Pour les contrôles fenêtre enfant mentionnés ci-dessus 6 classes sont prédéfinies: BUTTON, COMBOBOX, EDIT, LISTBOX, SCROLLBAR, et STATIC. Ces "contrôles sont" Enregistré en appelant la fonction InitCommonControls.

Le Message Queue et Message Loop

Quand un programme Windows Commence l'exécution L'OS crée un "message queue" pour le programme. Ce Message Queue ou sont stockés les messages de toutes les fenêtres q'un programme pourrait créer.Une application Windows inclut une courte partie Appelée "message loop" Qui récupère les messages De la Message Queue en appelant la fonction et GetMessage Les expédie à l'intérieur en appelant par DispatchMessage la fenêtre . Les autres messages sont envoyés directement à La procédure Windows, sans être placé en Message Queue (file d'attente).

On dit que les Message Queue sont affichés dans Message Queue nonqueued et que les messages sont envoyés vers la procédure Windows.Message Queue en général est le résultat de la saisie de l'utilisateur sous forme de raccourcis clavier, les caractères en rapport à la frappes de touches, mouvements de souris et clics de souris, mais également qui inclu les messages à affichage colorés (WM_PAINT) et le message de sortie (WM_QUIT). Nonqueued messages sont tout le reste et sont souvent le résultat d'appeler les fonctions Windows.Par exemple, lorsque les appels WinMain fenêtres crée la fenêtre et dans le processus envoie la fenêtre WM_CREATE intérieur un message.

Dialog boxes et Child Window Controls

Les Boîtes de dialogue ne sont rien de plus que la normale, qui sont des fenêtres Conçues pour fonctionner avec les contrôles de fenêtre enfant. Elles peuvent être Utilisées comme un périphérique d'entrée à la fenêtre principale de votre app Ou comme étant la fenêtre principale . La taille et la disposition d'une boîte de dialogue et ses contrôles sont définies dans un modèle de boîte de dialogue des ressources Script. Cela peut être écrit manuellement mais c'est bien mieux de le faire À l'aide de Visual WinAsm's Resource Editor.

Quand un programme invoque une boîte de dialogue basée Sur un modèle, Windows est responsable de la création des Boîte de dialogue de fenêtre popup et les contrôles de fenêtre enfant , et fournissant une fenêtre pour la procédure d'instruction des messages de la boîte de dialogue , y compris toutes les entrées clavier et souris. Depuis le contrôle de la fenêtre enfant et de leur propre processus de la souris et du clavier Les messages et les asocies à la fenêtre parente lors de leurs Modifications, cele allège énormément le travail des programmeurs . Le code de Windows pour tout cela est Parfois appelé le «gestionnaire de la boîte de dialogue."

Beaucoup de messages traités par le Boîte de dialogue de gestionnaire sont aussi passés à une procédure de rappel Au sein de votre propre programme appelé une boîte de dialogue ou la procédure de dialogue procedure.A procédure est similaire à celle d'un Procédure dans la fenêtre Windows qui envoie des messages à la Procédure quand il a à donner l'information ou les tâches à accomplir . La plupart des procédures de la boîte de dialogue du processus WM_INITDIALOG WM_COMMAND message et ses messages envoyés par les contrôles.

Contrairement à une Windows Procédure, une boîte de dialogue de la procédure n'appel jamais la fonction DefWindowProc. Au contraire, elle renvoie la valeur TRUE Si elle traite un message ou FALSE s'il ne le fait pas. En d'autres termes, si une procédure de dialogue ne traite pas un message, il doit retourner FALSE directement à Windows qui traite ensuite les messages Interne. La seule exception à cette règle est WM_INITDIALOG message. La procédure de la boîte de dialogue doit retourner TRUE pour Windows directement à traiter bien avant la WM_INITDIALOG Message.

Vous pouvez faire communiquer un contrôle fenêtre enfant et une Boîte de dialogue (par l'intermédiaire de son unique Numéro de control ID) en utilisant la fonction SendDlgItemMessage.Win32.hlp Disponibles sont dans la liste des messages de Windows, mais fournit aussi - Le contrôle de plusieurs fonctions de l'API spécifique pour obtenir et définir des données Rapidement, par exemple GetDlgItemText, etc CheckDlgButton Ces fonctions de contrôle spécifiques sont prévues pour la commodité du programmeur et de rendre le code source plus Lisible. Recourir à SendDlgItemMessage seulement si il n'y a aucun Contrôle des appels d'API .

Les 2 principaux types de boîte de dialogue

model - Fréquente, eg "About" box dans le menu d'aide. L'utilisateur ne peut pas Basculer entre les Boîtes de dialogue et une autre fenêtre de votre programme. L'utilisateur doit Répondre à la boîte de dialogue d'abord. L'utilisateur peut, toutefois, Passer à un autre programme. Créée en appelant DialogBoxParam Qui retourne une Valeur seulement après que la boîte de dialogue soit détruite. Si vous incluez DS_SYSMODAL le style de modèle dans la boîte de dialogue , il se Produira un model de Système de dialogue qui doit être terminé Avant que l'utilisateur ne puisse faire quoi que ce soit d'autre dans Windows.

modeless - eg text "find" Dans la case Traitement de texte. Permettent à l'utilisateur de basculer entre la boîte de dialogue Case et la fenêtre qui l'a créée ainsi que tout autre Programme. Créée en appelant CreateDialogParam qui retourne immédiatement avec la fenêtre de dialogue .

Il ya plusieurs autres Différences importantdes :

1. Boîtes de dialogue modale en général d'une Légende de la barre de menu et d'un système de boîte de permettre à l'utilisateur de se déplacer La boîte de dialogue dans une autre zone de l'écran en utilisant soit La souris ou le clavier. Vous n'avez pas besoin de cela avec un modale Boîte de dialogue car l'utilisateur ne peut rien faire dans la Fenêtre sous-jacente de toute façon.

2. Si vous ne lui faites pas comprendre par WS_VISIBLE , ni appel ShowWindow, la boîte de dialogue modale ne sera pas Affiché du tout.

3. Contrairement aux messages des boîtes de dialogue modales Et les boîtes de message, les messages des boîtes de dialogue modale à venir Par le biais de votre programme le message de la file d'attente qui doit être modifié Faire passer ces messages à la fenêtre à l'intérieur de la boîte de dialogue.

Boîtes de dialogue communes

Windows prépare et prédéfinie "commun" pour les boîtes de dialogue L'utilisation par vos applications, y compris les fichiers, d'impression, de couleur, Police, la recherche et les boîtes de dialogue. Ils sont là pour Fournir une interface utilisateur standardisée et résider dans Comdlg32.dll afin de les utiliser, vous devez lier à Comdlg32.lib. Vous créez ces boîtes de dialogue en appelant Fonctions appropriées dans la bibliothèque commune de dialogue. Pour ouvrir la boîte de dialogue Fichier, c'est GetOpenFileName, pour "sauvegarder dans" dialog c'est GetSaveFileName, pour l' "impression" dialog c'est PrintDlg Et ainsi de suite. Chacune de ces fonctions prend un pointeur vers une structure Comme son paramètre. Ils sont énumérés dans Win32.hlp.

WinMain, Handles et Instances

L'EntryPoint d'un programme Windows est la fonction WinMain, Qui est appelée par le système d'exploitation afin de Commencer l'exécution du programme a été chargé à partir du disque En mémoire par le chargeur Windows. En C / C + + des programmes, le Label "WinMain" doit être utilisé en assembleur, mais un label peut Être utilisés. Quand un exécutable est mappé en mémoire par le Fenêtres chargeur, la version en mémoire que l'on appelle un module. Windows 32 bits dans l'adresse de départ en mémoire (linéaire Adresse) où le fichier de cartographie Commence équivaut Le module manipuler.

A handle est simplement un 32bit (DWORD-sized) Nombre qui utilise le système d'exploitation D'identifier de façon unique chaque objet qu'il crée par exemple un Fenêtre ou le bouton-poussoir. Vous obtenez la poignée de Windows et Puis utilisez le levier à d'autres fonctions. Le module identifie gérer le programme. Le module est également manipuler Connu sous le nom de «instance handle" qui est le premier paramètre WinMain prises par la fonction et est tenu d'en faire un argument Windows à d'autres fonctions.

Dans les premières versions de Windows, lorsque vous avez exécuté Plusieurs instances du même programme, toutes les instances de l' L'application du code partagé et à lecture seule. Un programme Pourrait déterminer si les derniers cas de se dirigeaient Et puis sauter certaines corvées et déplacer des données de la précédente Exemple dans sa propre zone de données. Dans les versions 32 bits de Windows, ce concept a été abandonné. Le "précédent Exemple de faire "est resté le deuxième paramètre de WinMain mais est maintenant toujours NULL Ou 0.

Easy GUI applications utilisant les boîtes de dialogue

Boîtes de dialogue peuvent être utilisés comme données d'entrée Dispositif de la fenêtre principale de votre application ou de la fenêtre principale Elle-même. Ceci est réalisé en appelant à créer DialogBoxParam Une boîte de dialogue modale sans fenêtre parente. Cette approche Simplifie considérablement la conception du programme en utilisant une boîte de dialogue Procédure au lieu d'une fenêtre de la procédure et rend un message loop inutile puisque les messages sont envoyés directement à l' intérieurde la boite de Dialogue. La boîte de dialogue peut être créé dans un script Ressource dans un éditeur de visuels en utilisant les Contrôles fenêtre enfant qui effectuent la plupart de leur propre traitement du message. Vous N'avez même pas besoin d'enregistrer une classe Windows. Nous utiliserons cette Approche pour les exemples de ce tutorial.


ARTeam Tutorial

Programme Simple pour une boîte de dialogue "DialogBox"

Nouvelles Conceptions:

Création d'une dialogue box dans une application
Utilisation Child Windows Contrôles - boutons
Création d'un script ressource
Fonction prototypes
Utilisation Windows messages

Nouvelles fonctions API :

GetModuleHandle
DialogBoxParam
SetDlgItemText
SendMessage
EndDialog

Dans cette section, nous allons créer Un programme avec une simple boîte de dialogue avec un bouton Affiche un message et un bouton de sortie. Début WinAsm et d'ouvrir une nouvelle Exe projet. Nous allons d'abord créer la boîte de dialogue de modèle WinAsm utilisant Visual Ressource Editor. Ceci affiche les contrôles sur une grille L'intérieur de votre boîte de dialogue telle qu'elle apparaîtra dans votre programme. La Boîte à outils de dialogue et barres d'outils (à partir du menu Affichage) permettent D'ajouter plus de contrôles et disposez-les visuellement. Plutôt Que de taper sur une ressource script manuellement, toute la boîte de dialogue Peut être conçu de cette manière.

Soit Sélectionnez Projet>Add New Rc, ou clic-droit dans la barre de l'explorateur project et selectionnez Add New Rc. Visual est activé en mode par Défaut donc de cliquer sur "Add New Dialog" (Le 1er bouton dans la Toolbox bar) Et vous devriez voir ceci:

Maintenant, nous allons ajouter une Captions, 2 boutons et Une Editbox. Cliquez sur l'Onglet "Resources" en bas à droite de L'écran, double-cliquer la case et entrez Caption "Simple Dialog Box Program". Maintenant cliquez sur le bouton "Edit" (4èm bouton dans la Toolbox) Et vous verrez dans une boîte quelque chose comme ceci:

Maintenant dans la barre de l'explorateur, cliquez sur IDC_EDIT1002 cela l'efface et en dessous de son champ Texte. Finalement, cliquez sur le "Bouton" dans la barre de Toolbx et ajoutez 2 boutons, et changez le texte des boutons par "Say Hello" et "Exit"

Les boutons de la barre d'outils de dialogue vous permettent l' Alignement et la modification de la taille des contrôles pour les rendre plus agréables à L'œil. Pour voir le script de la ressource que WinAsm a créé pour nous, désactivez le mode visuel en pressant la touche F12, désélectionnez"Visual Mode" Ressources dans le menu ou en appuyant sur le Bouton de la barre d'outils :

La désactivation du mode visuel vous permettra également de créer un script ressource manuellement ou d'utiliser celle que j'ai Inclus dans le code source de la section et de la coller dans le script de dialogbox1.rc:

Afin de commencer à écrire le code pour Boîte de dialogue de notre modèle nous avons besoin de connaître les noms et ID de contrôle de la boîte de dialogue, la saisie et les boutons Comme indiqué dans les 4 premières lignes du script de la ressource ci-dessus.

Sélectionnez Untitled1.asm dans la barre de l'explorateur de projet et collez le dans dialogbox1.asm venant du sourcecode section:

Le petit signe "+" à gauche du bouton montre le début et la fin de la procédure de la dialogbox. Cliquant dessus cela vous montre l'ensemble de la procédure comme indiqué ci-dessous:

Finalement sauvez votre projet,script ressource et fichier ASM et pressez le bouton Go All. Vous devriez voir ceci lorsque vous Cliquerez sur le bouton "Say Hello".

En cliquant sur "Go All" Cela compile le Script de ressources, ainsi que l'assemblage et ses liaisons. Si vous Modifier la mise en page de la boîte de dialogue ou de ses contrôles, vous pouvez Recompiler le script sans ressource ré assembler et Faire un lien en sélectionnant>Compile RC. Maintenant, nous allons analyser Le code. L'en-tête et les dossiers dans la bibliothèque devrait Être explicites maintenant.

Prototypes

DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD - En 1er on déclare la fonction prototype pour DlgProc Donc nous Pouvons nous référer à l' addr lorsque nous invoquons DialogBoxParam plus tard.

Variables

Notre Message string - est terminée par null "Hello World!".

hInstance - (est défini dans windows.inc DWORD) uninitialised mais aura le handle de notre module de la Boîte de dialogue pour l'appel à DialogBoxParam.

Code Programme

invoke GetModuleHandle - Récupère le module handle ou l'instance handle de notre programme. C'est l'ID de votre programme et est Utilisée comme un paramètre à plusieurs fonctions de l'API de notre programme Doit appeler, donc il est d'usage de le récupérer au début de notre programme.

mov hInstance,eax - Déplace le Résultat de l'API du GetModuleHandle Eax dans une variable pour cela, nous avons déclaré ci-dessus (au retour d'une fonction Win32, la fonction nous retourne la Valeur, le cas échéant, qui est placée dans eax).

invoke DialogBoxParam - Crée un modèle de boîte de dialogue et retourne dans la boîte de dialogue Est détruite. Prend 5 paramètres: l'exemple de traiter la demande, Boîte de dialogue de modèle, la fenêtre parente manipuler, l'adresse De la boîte de dialogue intérieur, de dialogue et de données spécifiques.

invoke ExitProcess - L'instruction finale comme décrit dans notre chapitre skeleton.

Dialog Procédure

La procédure de dialogue est une fonction de rappel de service qui traite des messages envoyés à la boîte de dialogue. Il a quatre paramètres et retourne TRUE ou FALSE selon si vraiment il traite le message. Dans ce cas les commandes de fenêtre enfant envoient des messages à la procédure de dialogue en l'appelant et passant le message .

La première ligne consiste en déclaration de fonction en DlgProc. Notez les paires de type de paramètres qui suivent la directive PROC. Les paramètres consistent en Handle de fenêtre, un identificateur de message (uMsg) et deux paramètres de message (wParam et lParam) qui spécifie des informations de message supplémentaires.

Quand les commandes de fenêtre enfant envoient un message, le paramètre uMsg contient WM_COMMAND et wParam contient le contrôle ID. Le traitement de message est accompli en utilisant une série IF-ELSEIF-ENDIF qui vérifie d'abord pour voir si uMsg contient un message WM_COMMAND d'une des commandes ou un message WM_CLOSE.

Si le message est WM_CLOSE alors EndDialog est appelé pour détruire la boîte de dialogue. EndDialog ne le fait pas immédiatement, il met seulement un flag pour le directeur de boîte de dialogue interne et continue à exécuter les instructions suivantes.

Si le message est WM_COMMAND alors une série IF-ELSEIF-ENDIF consécutive vérifie la valeur dans wParam pour voir comment répondre - dans ce cas si le bouton "Say Hello" (ID 1003) ou Le "Exit" bouton (ID 1004) à été pressé.

SetDlgItemText - Met le texte du contrôle (l'editbox ID 1002).

SendMessage - Envoie un message WM_CLOSE pour la procédure de dialogue.

La ligne suivante XOR eax,eax juste zeroes eax avant RET L'exécution de retours à DialogBoxParam et par la suite ExitProcess est appelée.

Analyse dans le Débuggeur

Comme d'habitude nous devrions ouvrir notre application dans Ollydbg pour voir comment le code a été assemblé :

Puisque nous codons en ASM, nous obtenons un désassemblage propre et agréable dans Ollydbg. Le premier crochet inclut à gauche les 4 premières lignes de notre code et la deuxième crochet inclut la procédure de dialogue qui est moins facile à comprendre au premier coup d'œil. MASM a converti IF-ELSEIF-ENDIF en haut niveau construit dans comparent (CMP) et instructions de saut conditionnel. Comme auparavant, le bloc final d'instructions mises en surbrillance en jaune n'est le saut thunks indiquant la Table d'Adresse d'Importation.

Notes

Le champ de Nom de la boîte de dialogue et ses commandes peuvent être changés en quelque chose de plus significatif par double-clic dans le rédacteur visuel. Le script sera changé en conséquence, eg :

On peut alors déclarer ces noms comme constantes dans le fichier ASM et utilisé dans le code au lieu du contrôle numérique IDs pour améliorer la lisibilité :

Il y a quelques particularités quant au paramètre du modèle de boîte de dialogue a passé à DialogBoxParam. Si le nom de boîte de dialogue et le contrôle ID sont définis dans le script de la ressource (Comme ils sont à la ligne 1 de l'exemple WinAsm a fait pour nous) Alors on peut passer le contrôle ID seul à DialogBoxParam. Si on déclare aussi le nom et le contrôle ID comme une constante dans le fichier ASM alors on peut passer le nom à DialogBoxParam. Si le nom et le contrôle ID est pas définis dans le script de la ressource alors on peut déclarer le nom comme une variable string dans la section .data du fichier asm et passé à DialogBoxParam avec l' addr opérateur.

Inclusion de la ligne include resource.h Au début du script de la ressource permet l'utilisation des noms descriptifs plutôt que les numéros des Styles qui définissent l'apparence du dialogue et de ses commandes.


ARTeam Tutorial

Boîte de dialogue avec Icône et Menu

Nouveaux Concepts:

Ajout d'une icône et menus à une boîte de dialogue
Utilisation de styles de fenêtres
L'utilisation d'un manifeste pour appliquer le Style Visuel XP aux commandes

Nouvelles fonctions API:

LoadIcon
GetDlgItem
SetFocus
GetDlgItemText

Nous nous pencherons sur le dernier exemple en ajoutant une icône, un menu et un certain code pour montrer le texte tapé dans la boîte d'édition dans une boîte de message. Nous commencerons aussi par le curseur dans l'editbox.

Créez d'abord nouveau projet WinAsm en ajoutant un nouveau fichier de rc. changez le mode visuel (F12) colle alors dans dialogbox1.rc du dernier projet. Tournez maintenant le mode visuel en arrière sur et cliquez sur l'étiquette de ressources dans la barre d'explorateur. Mettez d'abord en évidence la boîte de dialogue et le titre en double-cliquant pour le changer "la Boîte de dialogue avec le menu d'Icône.

Maintenant nous voulons ajouter un menu appelé "Menu" avec 4 articles; "Get Text", ""Clear Text"", un séparateur et "Exit":

1. Cliquez sur le deuxième bouton de la barre d'outils dans la barre d'explorateur (New Menu) et entrez "MyMenu" le nom de menu et pour le premier article de menu entrez "Menu" dans la boîte de Titre, efface le Nom et cliquez insert.

2. Pour l'article de menu suivant entre le Titre "prend le Texte", le Nom "IDM_GETTEXT", clic droit sur la flèche jaune Insérez ensuite:

Ajoutez maintenant encore 3 articles de menu pour le ClearTexte, un séparateur (tapez "un-" dans la boîte de Titre et videz la boîte de Nom) et la Sortie et dentelez-les avec les flèches jaunes. Cliquez finalement sur OK :

Ensuite nous dirons à la boîte de dialogue d'utiliser notre menu et changerons le style de dialogue :

1. Cliquez sur OK alors le clic des Dialogues dans la barre d'explorateur et mettez en évidence IDD_DLG1001. Clic dans le champ de menu et sélectionnez "MyMenu".

2. Clic dans le champ de Style et le double-clic DS_CENTER suivi par OK. Cela fera que notre boîte de dialogue apparaîtra dans le centre de l'écran plutôt qu'en haut à gauche. Beaucoup de ces paramètres sont utiles, eg tournant de WS_SIZEBOX empêche que notre boîte de dialogue soit redimensionnée qui la ferait paraître un peu étrange.

Ensuite nous ajouterons une icône (inclus Ici - Extrayez-le dans votre dossier de projet). Le clic sur le bouton pénultième dans la barre d'explorateur, le clic ajoute et navigue votre icône. Remplissez le d'un Nom et ID par double-clic dans les champs alors cliquez sur OK :/font>

fermez finalement le mode visuel pour voir le script. De nouvelles parties sont encadrées en rouges. Si n'importe lequel du nouveau contrôle IDs a des duplicata les changer maintenant - ils doivent tous être uniques (encadré en vert):

Sauvez votre nouveau script de la ressource comme dialogbox2.rc et maintenant nous verrons ce que le code a ajouté pour utiliser ces ressources. clic sur l'explorateur de projet, choisissez Untitled1.asm et la collez dans le code de dialogbox2.asm. Comme vous pouvez voir il n'a pas beaucoup changé sauf quelques nouvelles variables:

Et la procédure de dialogue traite des messages du menu:

Go All Devrait vous le donner:

Maintenant nous allons analyser le code.

Variables

De nouvelles variables incluent des strings de Titre pour notre messagebox, buffer - un tableau non initialisé de 512 octets pour recevoir le texte de l'editbox dans et le contrôle IDs des articles de menu et l'icône.

La Procédure de Dialogue

Le premier message que notre procédure de dialogue reçoit de Windows est WM_INITDIALOG. Si nous le traitons avec notre procédure de dialogue nous pouvons mettre n'importe quel code d'initialisation ici.

LoadIcon - Charge la ressource d'icône indiquée de l'exécutable et retourne son handle dans eax. Le message WM_SETICON charge la procédure de dialogue d'associer l'icône avec l'handle indiquée à la boîte de dialogue.

GetDlgItem - Trouve l'handle du contrôle indiqué (l'editbox ici) et le retourne dans eax.

SetFocus - Le force à saisir le centre au contrôle avec l'handle indiquée (de nouveau l'editbox ici donc l'utilisateur peut y taper le texte immédiatement).

Les lignes suivantes de code traitent des messages WM_COMMAND comme auparavant. En plus de l'arrivée de la fenêtre enfant contrôle ceux-ci sont maintenant produits si l'utilisateur a choisi un article de menu. Si il vous arrive d'utiliser les mêmes codes d'ID pour des menus et des commandes de fenêtre enfant, vous pouvez les différencier entre elles en examinant la valeur de lParam, qui sera 0 pour un article de menu:

 | Low word of wParam | High word of wParam | lParam
Menu | Menu ID | 0 | 0
Control | Control ID | Notification code | Handle fenêtre enfant

Si lParam est le zéro, le courant WM_COMMAND le message est d'un menu. Vous ne pouvez pas utiliser wParam pour le différencier entre un menu et un contrôle depuis le menu ID et contrôler ID peut être identique et le code d'avis peut être le zéro.

Dans notre cas nous avons IDS différent donc nous pouvons traiter tous les messages dans la même boucle sans devoir examiner de ceux avec un zéro lParam une seconde si la boucle. Nous revisiterons le concept de spliting les mots d'ordre hauts et bas des messages pour obtenir le code d'avis dans le chapitre custom keygen .

La seule nouvelle fonction que nous avons utilisée est GetDlgItemText:

Utilisation du Style Visuel d' XP

Vous pouvez remarquer bien que les grands traits des boutons dans le rédacteur de Ressource WinAsm aient les coins arrondi dans le style XP, une fois l'exécutable compilé on voit qu'il remet à la place le style boutons carré. Pour utiliser le nouveau Style Visuel XP avec les commandes communes vous devez utiliser un fichier de manifeste. C'est une norme(un standard) xml (la langue de majoritaire extensible) le fichier qui doit être édité seulement pour inclure le nom de votre application. Le nom du fichier de xml lui-même peut être quoi que ce soit.. Il devrait être placé dans le même dossier que les fichiers source et est spécifié par une ligne seule dans le script de la ressource.

Le manifeste peut être ajouté au projet en cliquant sur le label de la Ressources dans la barre d'explorateur du projet, cliquez Ressources Ajoute alors, et l'ajout du fichier de manifeste. Un nom de ressource n'est pas nécessaire mais l'ID pour le manifeste doit toujours être 1 ou il ne marchera pas:

Cela ajoute la ligne suivante au script de la ressource juste au-dessous de la ligne de d'icône (qui pourrait être fait manuellement):

1 24 DISCARDABLE "dialogbox2.xml"

Sauvez finalement dialogbox2.rc de nouveau et il devrait correspondre à la version dans la section de code source. Maintenant en recompilent vous devriez voir:

Notez les boutons sont arrondis avec le contour bleuâtre. Le fichier de manifeste est aussi inclus dans la section de codes source. Juste pour la référence le contenu consiste en ce comme suivants:


ARTeam Tutorial

Model d'un Keygen Simple

Nouveaux Concepts:

Utilisation du presse-papiers avec messages de fenêtres
Écriture de votre fonction propres
Sauts Conditionnels(Conditional jumps) et Labels
Boucle et Labels Anonymes

Nouvelles fonctions d'API:

SendDlgItemMessage
lstrlen

Dans ce tutorial nous ferons un keygen simple et ajouterons le code par exemple l'algorithm rippé de la génération de serial de n'importe quelle routine de protection. Nous ajouterons aussi le code pour copier le produit de la génération de serial au presse-papiers.

Gonflez à bloc WinAsm comme d'habitude, créez un nouveau projet norme(standard) exe et ajoutez un nouveau script de ressource. Au label de la Ressources du clic de barre d'explorateur de projet Ajoutez le Nouveau Dialogue et ensuite des Ressources ,ajoutez notre icône (comme dans l'exercice précédent).

Ajoutez 2 commandes d'édition pour entrer à notre nom et montrer notre serial, 2 commandes statiques pour labeler l'editboxes et 3 boutons comme indiqué :

Changez le Style du dialogbox pour inclure DS_CENTER, les 2 labels statiques pour inclure SS_CENTERIMAGE et editbox serial pour inclure ES_READONLY. Le script est inclus dans la section de code source comme simplekeygen.rc

Maintenant ajoutons le fichier simplekeygen.asm dans l'explorateur de projet et cherchez-le dans la section de code source. Voici le corps principal de l'application:

Suivi selon la Procédure de Dialogue:

Et la procédure génère le calcule du numéro du serial:

Maintenant nous allons analyser ce code-ci.

Variables

Il y a 2 variables de string non initialisées dans lesquels sont stocker la saisie de l'utilisateur pour (name) et le serial calculé.

La Procédure de Dialogue

Nous avons des routines pour traiter la saisie de l'utilisateur de nos 3 boutons.

La réponse avec le bouton Generate, Nous ne faisons simplement que prendre la saisie du Nom de l'utilisateur dans son editbox et le stockons dans le NameBuffer en tant que variable, appellée pour Produire la routine qui génère et calcul le serial dans l'editbox Serial.

Le bouton Copy fait en sorte que le serial dans l'editbox serial soit copié dans le presse-papiers. Cela est réalisé entièrement en envoyant des messages de fenêtres à editbox serial. C'est beaucoup plus facile que l'écriture d'instructions d'assemblage d'exécuter la même chose que le code est plutôt malpropre. Notez que de Win32.hlp le texte à être copié doit être choisi d'abord :

et le code pour choisir le contenu entier d'une editbox est :

Il est important de noter qu'il y a des fenêtres de messages pour accomplir un nombre énorme de tâches dans des fenêtres sans écrire un long code pour faire ce même travail.

Le bouton Exit fonctionne comme décrit précédemment.

La Procédure qui génère et labels

La fonction qui génère est un exemple d'une simple routine qui change complètement l'ordre des lettres dans la string entrée dans l'editbox du Nom. Notez que nous ne donnons pas de paramètres à la fonction et elle ne rend pas de valeur comme telle. Il n'y a donc aucun avantage d'écrire un prototype et l'appeler avec invoke.

La routine a été d'abord écrite sans ces lignes

test eax,eax
jle NOINPUT
NOINPUT:

Mais en cliquant sur Generate ça cause un crash si NameBuffer est vide (c'est-à-dire il n'y a aucune saisie de l'utilisateur). La première instruction lstrlen Calcule la longueur de la string dans NameBuffer et met le résultat dans eax. Le fichier Win32.hlp : "la fonction lstrlen place la longueur dans des octets (la version d'ANSI) ou des caractères (Unicode version) de la string indiquée (aucun caractère entré donc nul ) ".

Test eax,eax Met le flag à zero si eax est zero et jle est un saut conditionel (Jump if Less or Equal) => (Saute si inférieur ou égal) saute vers le label NOINPUT (Aucune entrée) et l' instruction ret si le flag est mis à zero. Autrement dit la fonction Generate retourne juste sans exécuter aucune de ses instructions internes au lieu de cracher(planter) s'il n'y a aucun caractères saisis par l'utilisateur.

Les labels sont des noms que vous pouvez donner dans une partie du code et l'assigner une instruction de saut conditionnelle . Quand un label est défini il est suivi par deux-points " : ", mais quand le label apparaît derrière l'instruction de saut on ne met pas les deux-points " : " .

Si la longueur de la string dans eax est zéro alors elle est copiée dans ecx par l' instruction mov . ECX est généralement utilisé comme un compteur pour spécifier combien de fois l'instruction qui forme la boucle doit être effectuée - dans ce cas ECX sert à traiter chaque caractère de la string et de boucler tant que tous les caractères de la string n'ont pas été énumérés par ECX .

Ensuite les adresses mémoire du NameBuffer et des variables SerialBuffer sont copiées dans ESI et EDI. Ces deux registres sont utilisés pour indexer la source et la destination pour des manipulations de string.

@@: Déclare un label anonyme. Dans de longues routines utilisant un tas de sauts ou de boucles il est encombrant de taper des noms différents pour tous les labels nécessaires donc MASM peut utiliser des labels anonymes pour des sauts courts ou des boucles. Utiliser ce label dans le code @f jumps forward et le suivant @@: et @b jumps backward Au plus proche @@:

Finalement la string changeant complètement la routine marche comme cela :

D'abord le compteur ecx est dec eleve à 1 pour que la boucle finale finisse sur un zéro au lieu de 1. Eg si la longueur de string du Nom était 6 caractères alors ecx fera maintenant la boucle de 5 à 0 et la routine exécutera 6 fois.

ESI contient l'adresse du premier octet du (caractère) du NameBuffer quand ECX=0, ESI+ECX pointe le premier caractère et quand ECX=5, ESI+ECX pointe le caractère final.

La première instruction mov copie l'octet final dans le NameBuffer dans l'octet bas du registre d'edx (appelé dl) .

La seconde instruction mov copie cet octet dans le premier octet du SerialBuffer (stocké dans edi).

Ensuite la valeur d'edi est inc eleve à 1 pour indiquer au deuxième octet du SerialBuffer et va vers pour la boucle suivante de la routine .

Pour finir une opération logique OR Est exécuté sur ECX qui mettrait le flag à zéro si ECX avait atteint le zéro. L'instruction ja (jump if above) saute si le flag est zéro ,flag n'est pas mis. Autrement dit la routine garde la formation des boucles jusqu'à ce qu'ECX soit arrivé à 0 quand il continue sur à l'instruction ret et sort en arrière par la procédure de dialogue.

C'est une façon très simple d'écrire une fonction. Vous pouvez passer vos propres fonctions en un certain nombre de paramètres et avoir la fonction qui donne un résultat ou un succès / le flag d'erreur dans EAX comme les fonctions d'API le font. L'écriture d'un prototype pour la fonction autorisera l'utilisation d'invoke. Nous le découvrirons dans la page suivante de ce tutorial et personnaliserons notre keygen avec du graphisme et du son.


ARTeam Tutorial

Customisation d'un keygen

Nouveaux Concepts:

Ajout d'une ressource bitmap dans le Contexte d'une boîte de dialogue
Modification de la réponse et à mise à l'épreuve du clic de la souris
Utilisation des effets de transparence de Win2k/XP
Utilisation de Table Extractor de Thigo
Ajout d'un Module son d'extention (xm) chiptune
Écriture de fonctions plus sophistiquées - utilisation de prototypes, arguments et invoke

Nouvelles fonctions d'API:

SetWindowPos
GetWindowLong
SetWindowLong
SetLayeredWindowAttributes
ShowWindow
Sleep
wsprintf
lstrcat
RtlZeroMemory

Dans cette partie de ce tutorial nous personnaliserons notre keygen en ajoutant un contexte bitmap et changeant les Styles de Fenêtre de notre dialogbox et ses commandes pour nous débarrasser de la limite merdique et du titre et utiliserons des boutons transparents. Nous changerons aussi la conception légèrement et échangerons le bouton Generate pour un bouton About. Nous ferons générer le serial automatiquement et changera à chaque fois que le texte sera saisi par l'utilisateur.

Nous utiliserons comme exemple SuperCleaner 2.9 disponible ici: http://www.southbaypc.com/download/CleanSetup.exe

C'est plus commode par ce que la routine du serial est rippée de la cible presque directement sans faire aucun changement donc il est plus facile de voir comment il est converti pour notre keygen ici. Il y a des tutoriaux disponibles sur le site ARTEAM pour cracker les versions précédentes de cette cible et la routine du serial n'a pas changée depuis.

Avant que nous ne concevions quoique ce soit d'abord concevons notre boîte de dialogue et écrivons ensuite le code concernant les ressources. Nous commencerons par le logo ARTeam_Logo_4.bmp Et ajoutons du texte comme des labels pour l'editboxes et des boutons transparents. N'importe quel éditeur de photo devrait pouvoir faire l'affaire (j'ai utilisé ACDSEE). L'origine du fond pour les boutons vient du ("Fond d'écran du film Blade Runner ") il est conforme au texte sur le bitmap et a été téléchargé sur le Web gratuitement (pour installer de nouveaux fonds il faut les coller simplement dans le répertoire C\Windows\Fonts). J'ai renommé le résultat final en ARTeam_keygen2.bmp.

Décompressez les bitmaps du fichier zip dans votre dossier projet et créez un nouveau projet exe (standard) dans WinAsm. Ajoutez un nouveau script de ressource et dans l'éditeur de visual cliquez "Add New Dialog".

Changement de la Boîte de dialogue

Dans la barre de l'explorateur sous le label de la ressources, mettez en surbrillance le nouveau dialogue et éditez le Nom pour qu'il devienne IDD_KEYGEN. Double-cliquez et supprimez le champ du Titre. Maintenant double-cliquez sur le Style et assurez-vous que les entrées suivantes only (seulement) soient mises à (Yes) Oui : WS_POPUP, WS_VISIBLE et DS_CENTER.

Cela nous débarrassera de la limite et fera le dialogue non redimensionnable de plus cela apparaîtra toujours dans le centre de l'écran. Faites glisser maintenant le dialogue pour l'agrandir jusqu'à ce qu'il remplisse environ la moitié de l'écran. Cliquez sur le bouton d'image sur la barre d'outils dans la Boîte à outils et avec la Croix faites glisser un rectangle assez grand pour faire tenir notre bitmap.

IDC_IMAGE1002 apparaîtra dans la barre de l'explorateur de projet. Cliquez sur le bouton de la ressources sur la barre d'outils et Ajoutez ensuite la boîte qui apparaît. Naviguez jusqu'à ARTEAM_KEYGEN.BMP et ajoutez-le. Double-cliquez et éditez le champ du Nom au LOGO et le champ de l'ID à 2002 et cliquez sur OK. Mettez maintenant en surbrillance IDC_IMAGE1002, Cliquez l'image du menu choisissez le LOGO et glissez-le. Maintenant le bitmap apparaît sur le dialogue.

Finalement il est question de redimensionner le bitmap et le dialogue donc ils se couvrent sans se chevaucher avant l'ajout des commandes. Ajoutez 2 editboxes à côté de Name: et Serial:

Pour le Nom l'editbox change le nom en IDC_NAME et le style pour que le suivant soit seulement mis à (Yes) Oui: WS_CHILD, WS_VISIBLE, WS_BORDER, WS_TABSTOP, ES_AUTOHSCROLL, ES_CENTER

Pour le Serial l'éditbox change le nom en IDC_SERIAL, add ES_READONLY Au style et l'édition du texte pour y inclure "SuperCleaner 2.9 Keygen".

Finalement ajoutez 3 boutons poussoirs un pour Generate, Copy et Exit. Pour le premier, élargissez-le et positionnez-le Pour qu'il couvre le mot Generate sur le bitmap. Le nom Ensuite est modifié en IDC_ABOUT, Texte à About Et de style pour le suivant only Sont mis à (Yes) Oui : WS_CHILD, WS_VISIBLE, WS_CLIPSIBLINGS, WS_TABSTOP, BS_FLAT.

Faites enfin de même pour les 2 autres boutons et vous devriez voir ceci:

Quand ce dialogue est montré les boutons seront invisibles indiquant le texte ci-dessous. Comparez votre script de la ressource à celui de la copie dans la section de sourcecodes. Maintenant collez la source de ce tutorial dans le dossier ASM de votre projet.

Puisqu'il y a beaucoup de nouvelle information ici que je n'analyserai pas ligne par ligne mais je parlerai de chaque section séparément. D'abord la procédure de dialogue :

Il serait plus commode si notre nouveau keygen pouvait rester sur toutes les autres fenêtres comme ça le code pour celui-là serra assorti à l'autre code d'initialisation de procédure de dialogue après le message WM_INITDIALOG et utilisera la fonction API SetWindowPos :

invoke SetWindowPos,hWnd, HWND_TOPMOST,0,0,0,0,SWP_NOACTIVATE + SWP_NOMOVE + SWP_NOSIZE

Car notre keygen n'a aucune limite ou caption bar, nous devons pouvoir le déplacer en cliquant dessus n'importe où. Nous faisons cela en manoeuvrant la réponse à la souris frapper-examinant l'IE. nous dupons des fenêtres dans la pensée de cela toutes les fois qu'un clic de souris se produit dans le secteur client de la zone de dialogue qu'il se produit vraiment au-dessus du captionbar ! Le code pour cela entre également dans la procédure de dialogue :

.elseif uMsg==WM_LBUTTONDOWN
invoke SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0

Le code d'initialisation pour notre dialogue contient également l'appel à la fonction enchaînée et les fonctions Music-Play qui sont expliquées ci-dessous. Cliquez sur le bouton About cela affichera une MessageBox avec nos détails. La fonctionnalité de copy fonctionne comme il l'a été décrit dernièrement dans ce tutorial et en cliquant le bouton Exit cela appellera les fonctions à l'effacement et arrêtera la musique jouée avant de détruire la zone de dialogue.

La nouvelle partie de cette procédure de dialogue est impliquée en filtrant dehors des messages de l'editbox de sorte que chaque fois qu' un caractère est écrit nous puissions répondre en appelant notre procédure Generate et de mettre à jour le serial. Quand le message de WM_COMMAND est envoyé par un contrôle, le composant wParam est divisé dans 2 moitiés. Le mot d'ordre bas(de commande basse) spécifie l'identificateur du contrôle et le mot d'ordre haut(de commande haute) porte un code d'avis.

D'abord nous regardons le mot d'ordre bas(de commande basse) (dans ax) pour voir si le message est de l'editbox nommé (IDC_NAME) alors nous devons regarder le mot d'ordre haut(de commande haute). Puisqu'il n'y a aucune manière en assembleur de regarder seul le mot d'ordre élevé d'un registre de 32 bits, nous avons pour décaler la partie d'ordre élevé dans la partie d'ordre réduit et la lire comme ceci. SHR EAX, 16 accomplit ceci en décalant le contenu du juste d'eax par 16 bit

Maintenant ax contient le mot qui précédemment était d'ordre élevé. c'est EN_CHANGE puis l'editbox qui nous dis que que le texte a été changé ainsi nous pouvons appeler notre procédure Generate. Omettre la présente partie du code produit quelques résultats étranges avec l'appel de Generate en réponse aux clics de la souris, etc.

Effets de Disparition(Fading Effects)

Pour faire en sorte que notre keygen apparaisse en fondu sur son ouverture et sa fermeture, nous avons 2 procédures appelées FadeIn et FadeOut. Pour ceux-ci nous aurons besoin de 2 prototypes,une variable DWORD non initialisée " Transparence" et des constantes suivant ce que nous devons définir comme elles ne sont pas déjà dans le fichier windows.inc : LWA_ALPHA, LWA_COLORKEY, WS_EX_LAYERED and DELAY_VALUE.

Pour utiliser les fonctions de transparence de win2k ou XP (c'est-à-dire non disponible sur les versions précédentes de Windows), nous devons utiliser une fenêtre à plusieurs Couches. Cela peut significativement améliorer la rapidité de l'exécution et les effets visuels pour une fenêtre qui a une forme complexe, anime sa forme,ou si vous souhaitez utiliser l'alpha mélangeant des effets. Le système automatiquement compose et peint les fenêtres posées et les fenêtres d'applications fondamentales. En conséquence, les fenêtres posées sont rendues sans à-coup, sans clignotement typique des régions complexes de fenêtre. En outre, les fenêtres posées peuvent être partiellement translucides, c'est-à-dire, alpha mélangées. Afin de faire une fenêtre posée, la fenêtre doit être créée avec ou ensemble pour avoir le modèle de fenêtres étendues par WS_EX_LAYERED.

La procédure FadeIn ressemble à ceci:

L'API GetWindowLong récupère des informations sur une fenêtre. Le type d'informations est spécifié par 2ème param - dans notre cas GWL_EXSTYLE demande le style de fenêtres étendue pour la fenêtre et est rendu dans eax. La ligne suivante ajoute la valeur représentée par WS_EX_LAYERED à eax. La 3ème ligne met cette valeur comme le style de fenêtres étendue pour la fenêtre.

L'API SetLayeredWindowAttributes met l'opacité et la transparence de la clef de colore d'une fenêtre posée. Dans ce cas le paramètre final spécifie une transparence (le mélange alpha) le cadre(l'arrangement) et 2ème 0 spécifient complètement transparent. La ligne suivante montre la fenêtre dans un format non activé et la ligne suivante déplace une valeur de 75 dans notre variable de transparence.

La boucle suivante d'instructions incrémente la transparence par 5 et met la transparence de fenêtre avec la nouvelle valeur à chaque boucle jusqu'à ce que la transparence s'étende 255 (la valeur maximale = opaque). Le sommeil laisse(quitte) un petit retard entre chaque boucle pour que l'effacement n'arrivent pas trop rapidement. Quand la transparence = 255 que la procédure retourne.

La procédure FadeOut montre presque la même chose et fonctionne dans l'ordre inverse :

Critiques de cette procédure fade :

Notre fonction fade-in est appelée en réponse au message WM_INITDIALOG étant envoyé à la dialogbox par l'OS. Le code placé est ici donc exécuté avant que le dialogue ne soit montré sur l'écran. Le résultat de c'est qu'un dialogue est faded-in - avant que le contexte bitmap et les commandes n'apparaissent tandis que la fonction fade-out - ne subit pas ce problème. Un autre (beaucoup plus simple) la solution peut être d'utiliser l'API AnimateWindow pour faire l'alpha-blending.La ligne suivante devrait suivre le message de WM_INITDIALOG :

invoke AnimateWindow,hWin,1000,AW_BLEND+AW_ACTIVATE

Et la ligne suivante devrait suivre le message WM_CLOSE avant l'appel EndDialog :

invoke AnimateWindow,hWin,1000,AW_BLEND+AW_HIDE

Cependant dans notre cas, bien qu'il donne un effet fade-in, les boutons transparents ne sont pas bien traités et sont dessiné comme visible au premier plan.

Son (Sound)

Cette partie vous expliquera comment ajouter un chiptune (ceux-ci peuvent être téléchargés librement sur (de nombreux sites Web) et créer le son en utilisant uFMOD. C'est essentiellement un player de module de traqueur rapide en bas en minuscule qui intervient comme une bibliothèque (uFMOD.lib) avec juste une API à 4 fonctions. UFMOD des fonctions d'importations de winmm.dll pour que la bibliothèque de cette DLL puisse être liée dans notre code source. Les instructions pour utiliser uFMOD sont contenues dans le fichier d'ufmodapi.inc et Il y a 2 exemples de code source inclus dans la distribution. UFMOD est une grande amélioration sur l'ancien player miniFmod.

Il y a 3 façons d'ajouter un chiptune à notre application :

1. Comme une table de données hexa collé directement dans l'asm code ou dans un fichier d'inc
2. Comme les données brutes du type RT_RCDATA dans la section de ressource .
3. Comme un fichier de .xm complet inclus dans la section de ressource.

Dans cet exemple j'ai opté pour la première option comme c'est facile et empêche le chiptune étant extrait par la ressource taillant le logiciel. J'ai couvert un exemple de la façon d'utiliser RT_RCDATA brut dans la section des ressource dans tutorial sur l'utilisation de régions (voir < un href = "arteam%20asm_page9_region.htm" > ici ). Cette méthode pourrait facilement être utilisée ici pour stocker les données de fichier de xm dans la section de ressource de notre exécutable.

D'abord nous devons convertir notre fichier de xm dans une table et le sauver comme un fichier include. Pour cela nous utilisons l'Extracteur de Table de Thigo. Lancez l'outil et choisissez File > Load and browse to DEFJAM3.XM:

Cela vous dit la taille de fichier donc vous devez juste le copier dans la boîte de Taille vide et vous remplir dans les autres boîtes comme suit :

Comme nous voyons dans listbox, le fichier dumpé comme result.txt. Puisque les données ont été disposées comme un tableau d'octets hexa appelés "la table" nous devons juste le renommer en chiptune.inc et il est prêt pour être utilisé.

Pour utiliser uFMOD nous devons inclure ufmodapi.inc et 2 bibliothèques ufmod.lib et winmm.lib. Ensuite nous devons inclure nos données xm en forme de table dans .data la section directement ou dans un fichier include chiptune.inc comme dans notre exemple (inclus dans la section de code source) .

Notez que chiptune.inc contient juste un tableau énorme de valeurs de taille hexa d'octet appelées "la table". Vous vous rappellerez qu'utilisant include est le même comme tapant du tableau directement dans le code. Car le PlaySong fonctionne nous devons savoir la taille des données xm donc la ligne suivante spécifie la taille dans la variable xmSize.

Le $ est défini dans les guides programmeurs MASM comme l'opérateur d'adresse actuel ou la valeur actuelle du compteur d'emplacement . L'une ou l'autre voie, soustrayant l'adresse du premier octet dans le tableau de table du $ donne la taille dans des octets .

Finalement nous devons ajouter des appels à la fonction de PlaySong quand la boîte de dialogue est initialisée et détruite. Cette fonction est définie dans ufmodapi.inc comme suit :

Puisque notre fichier de xm est inclus comme un tableau de données initialisées, il est chargé dans la mémoire quand l'exécutable est exécuté, ainsi le 3ème paramètre est XM_MEMORY. 2ème param est donc la taille des données dans la mémoire ou xmSize et le 1er paramètre sont un indicateur sur les données (l'adresse du premier octet de notre tableau). Pour arrêter le chiptune le jeu nous appelons simplement PlaySong de nouveau avec 0 paramètres.

La Procédure Generate

L' algorithme de la génération du serial commence en obtenant texte entré dans la boîte de Nom et le stockant dans NameBuffer. L'adresse de NameBuffer est copiée dans EDI et le nombre de caractères dans NameBuffer (et dans la boîte de Nom) est calculé et copié dans ESI. Le nombre de caractères est évalué pour voir si c'est le zéro. Si c'est le zéro la variable SerialBuffer et la boîte serial sont vidés et les retours ordinaires. Si a là été saisi, donc le commencement du calcul du serial.

Il y a 4 boucles, chaque addition ou multiplication notre nom avec une différente constante et l'enchaînement les résultats ensemble pour faire un serial de 4 sections.

Souvenez-vous à cette étape qu'EDI pointe au début de la string le Nom et ESI contient le nombre de caractères et qu'EAX et ECX ont été mis à zéro. La première constante (Hash1) est chargée dans EDX. Le premier octet de la string du Nom est copié (MOVSX == le mouvement avec le signe s'étend; regardez cela en haut si vous êtes intéressés) dans EBX et la valeur de gâchis est ajoutée.

Une fois que la section finale du serial a été calculé qu'il est ajouté aux autres parties et le serial, le résultat est montré dans la boîte du serial. J'ai inclus le désassemblage du code cible dans Ollydbg et montré ci-dessous pour que vous puissiez voir comment peu il différé de notre code d'asm ci-dessus :


ARTeam Tutorial

Utilisation de Régions et Données de Ressource

Nouveaux Concepts:

Régions et utilisation de RGN Générateur
Utilisation de données de ressource brutes
Utilisation de Brush
Répondant au Double-clique du bouton gauche de la souris

Nouvelles Fonctions API:

LoadBitmap
CreatePatternBrush
FindResource
LoadResource
SizeofResource
LockResource
ExtCreateRegion
SetWindowRegion
DeleteObject

Ce Tutorial nous montre comment réaliser des régions complexes et qui sont habituellement utilisées pour faire des formulaires de fenêtre personnalisées pour être appliquées à notre keygen ou à notre patch. La plupart des tutoriaux existants, pour ses formulaires de fenêtre personnalisées et des régions, sont appliquées aux thèmes des fenêtres entièrement pleines. Ici je n'ais pas pensé à vous donner un exemple concernant le cracking, donc je l'ai adapté au Tutorial N° 25 d'Iczelion (voir la section de référence) et montré comment cela peut être appliqué à une boîte de dialogue simple pour notre style rapide et facile en programmation.

Pour faire une fenêtre ou une boîte de dialogue en custom prenant une forme complexe, nous devons définir une région pour le plan de notre forme et ajouter ensuite la région à la fenêtre. Une région est un rectangle, un polygone, ou une ellipse (ou une combinaison des deux ou d'autres formes) qui peut être rempli, peint, inversé, encadré et utilisé du coup, pour exécuter la mise en teste (évaluant sur l'emplacement du curseur).

Il y a des fonctions API pour créer des régions simples (rectangulaire, elliptique ou polygonal) et combiner aussi de telles régions ensemble. Si nous voulons une forme plus complexe nous devons utiliser une région étendue. L'API pour créer une région étendue prend un indicateur sur un bloc de données de région :

HRGN ExtCreateRegion(
CONST XFORM *lpXform, // L'indicateur pour transformer des données
DWORD nCount, // La taille de structure contient des données
CONST RGNDATA *lpRgnData // Indicateur sur les données de région
);

Ces données de région consistent en un grand nombre (beaucoup de centaines) de régions rectangulaires combinées pour définir notre forme complexe. Heureusement nous ne devons pas calculer ces données de région nous-mêmes, car il y a un utilitaire appelée le Générateur de Région par Richard D'Oude qui le fera pour nous et la production des données nous donnera un fichier avec l'extension .rgn qui pourra être inclus dans notre exécutable comme une ressource.

Pour utiliser des données de ressource, nous devons trouver la ressource, charger la ressource, trouver la taille de la ressource et fermer finalement la ressource. Cela nous laisse avec un indicateur sur les données de ressource en mémoire qui peut alors être utilisée selon notre programme.

D'abord nous devons préparer un bitmap en choisissant un secteur dont nous voulons qu'il soit transparent et le faire se remplir avec une couleur particulière. Cela peut être fait dans n'importe quel éditeur de photo. J'ai utilisé l'exemple du Tutorial d'Iczelion (tweety78.bmp) qui est inclus dans les fichiers de ressource.

Ensuite nous devons créer nos données de région. Lancez le Générateur RGN, cliquez sur Browse pour choisir le bitmap et le Choix de cliquer pour ouvrir le panel de couleur. Vous verrez 2 places sur la droite - la supérieur indique la couleur qui sera actuellement transparente (noir par défaut) et l'inférieur est la couleur indiquée par la pipette (le pointeur de souris). Déplacez la souris sur un secteur rose et faites un clique avec le bouton droit pour choisir rose comme pour la couleur transparente. Cliquez finalement sur OK, Créant alors et sauvegardant votre fichier de région en region.rgn :

Ouvrir WinAsm et créez un nouveau projet et des scripts de ressource. Dans le nouveau script de ressource on ajoute un dialogue avec une taille assez grande pour contenir le bitmap. Changez ensuite les Styles pour que seulement WS_POPUP, WS_VISIBLE et DS_CENTER soient mis. Ajoutez un bouton carré minuscule avec un "?" pour son titre. Le bouton doit être placé sur une partie non transparente de l'image ou ce ne sera pas visible - cela peut demander un peu d'expérience car la position doit être correctement faites sinon vous ne pourrez pas voir la forme de l'image en lançant le Débuggeur (ou Lecteur):

L'autre alternative devrait ajouter l'image d'un bouton sur le bitmap comme nous avons fait dans le dernier Tutorial. Ajoutez ensuite le bitmap et les autres ressources du fichier de région mais ne changer en aucun cas ceux des noms. Dans ce cas le bitmap a 2001 ID et les données de région ont 2002 ID. Le script de ressource et le fichier en asm (region.rc et region.asm) sont inclus comme d'habitude dans la section du codes source. A coller dans le fichier en asm :

Le susdit devrait être très évident, mais dans la note, nous n'avons pas initialisé la variable prête pour traiter les données de ressource et un brush (le contexte bitmap). La procédure de dialogue est comme suit :

La première nouvelle ligne utilise LoadBitmap qui prend la ressource ID de notre bitmap (2001) et lui retourne un handle que nous pouvons ajouter à CreatePatternBrush. Un model de brush est simplement un bitmap que l'OS peut utiliser pour peindre l'intérieur des formes remplies. CreatePatternBrush retourne un handle au brush (le bitmap) pour lequel nous stockons dans une variable plus tard. Pour peindre notre dialogbox avec ce brush, nous devons répondre au message WM_CTLCOLORDLG et retourner le handle à notre brush. Le message WM_CTLCOLORDLG est envoyé à une boîte de dialogue avant que le système ne fasse apparaitre la boîte de dialogue pour permettre de mettre son texte et ses couleurs de fond.

Nous avons ajouté nos données de région à l'exécutable comme des données de ressource brutes. Pour l'appliquer à notre dialogue, nous devons d'abord créer une région de fenêtre étendue et la mettre ensuite à notre dialogue. Pour créer une région étendue, nous utilisons ExtCreateRegion qui prend un indicateur sur nos données de région et sa taille, puis retourne un handle à la région étendue finie que nous passons alors à SetWindowRegion.

Pour avoir accès à nos données de région nous devons trouver la ressource de son ID. Nous pouvons alors trouver la taille, le charger et le fermer ensuite pour obtenir un indicateur sur les données comme indiqué ci-dessus. On peut alors passer l'indicateur et la taille à ExtCreateRegion comme déjà dit.

C'est la façon standard d'avoir accès aux données de ressource et ne dépend pas du type de données en question. Si nous avions inclus un fichier audio comme des données de ressource brutes, comme dans le dernier Tutorial, alors nous y aurions accès exactement de cette façon.

Puisqu'il n'y a aucun bouton Exit dans notre fenêtre, il ne serait pas élégant de devoir la fermer en appuyant sur Alt+F4, donc nous tiendrons compte d'un Double-clique sur le bouton gauche de la souris pour Quitter. Ceci est accompli en traitant le message WM_LBUTTONDBLCLK comme indiqué ci-dessus.


ARTeam Tutorial

Patcheur & Offset

Nouveau Concepts:

L'utilisation des contrôles ListBox et CheckBox dans votre dialogue
Ouverture(Opening), Lecture(Reading), Ecriture(Writing) et Fermeture(Closing) des fichiers
L'Indicateur de Fichier

Nouvelles fonctions API:

CreateFile
CopyFile
SetFilePointer
ReadFile
WriteFile
CloseHandle

Dans ce tutorial, nous coderons un patcher autonome simple qui ouvre un fichier cible dans le dossier actuel du logiciel(le patch doit être placé dans le même dossier que la cible), qui contrôlera sur une certaine séquence d'octets à un offset donné et les recopie. Ceci est codé dans le patch à un offset correspondant. Si la séquence d'octets correct n'est pas trouvé, alors le patcher annoncera une erreur.

Il devrait aussi nous notifier si le fichier a déjà été patché. Il devrait y avoir une option pour faire une sauvegarde du fichier cible avant le patchage et si les attributs de fichier cible incluent seulement la lecture seule, cela devrait être mis en Lecture Ecriture. D'abord nous devons couvrir une certaine théorie.

Ouverture d'un fichier

La fonction de CreateFile crée ou ouvre un fichier et retourne un handle qui peut être utilisée pour avoir accès au fichier. Il peut aussi être utilisé pour ouvrir des objets incluant des dispositifs de disque, des consoles et des répertoires. Le paramètre dwCreationDisposition contrôle son comportement :

CREATE_NEW - Crée un nouveau fichier, échoue si le fichier nommé existe déjà
CREATE_ALWAYS - Crée le nouveau fichier, recopie n'importe quel fichier existant de même nom
OPEN_EXISTING - Ouvre le fichier, échoue si le fichier n'existe pas
OPEN_ALWAYS - Ouvre le fichier, crée un nouveau si le fichier n'existe pas

Chaque fichier ouvert a un indicateur de fichier qui spécifie l'octet suivant à être lu ou l'emplacement pour recevoir l'octet suivant a être écrit. Quand un fichier est ouvert pour la première fois, windows place l'indicateur de fichier au début du fichier. Comme chaque octet est lu ou écrit, windows avance l'indicateur de fichier. Une application peut aussi déplacer l'indicateur de fichier en utilisant la fonction de SetFilePointer.

Lire et écrire dans un fichier

Une application lit et écrit dans un fichier en utilisant le ReadFile et les fonctions de WriteFile qui exigent le handle du fichier à être ouvert comme ci-dessus. ReadFile et WriteFile lisent et écrivent nombre indiqué d'octets à l'emplacement indiqué par l'indicateur de fichier. Les données sont lues et écrites exactement comme donné; les fonctions ne formatent pas les données.

Quand l'indicateur de fichier atteint la fin d'un fichier et l'application essaye de lire la fin du fichier, aucune erreur n'arrive, mais aucun octet n'est lu. Donc, lisant des octets zéro sans une erreur signifie que le programme a atteint la fin du fichier. L'écriture d'octets zéro ne fait rien.

Fermeture du fichier

La fonction de CloseHandle ferme et invalide les handles, puis sort des ressources du système pour presque tous les objets. Le système fermera toutes les handles ouvertes à la fermeture du fichier, mais c'est toujours une bonne solution pour des programmes qui ferment leurs handles avant la fin.

Exemple de notre fichier cible

Nous utiliserons de nouveau SuperCleaner 2.9 comme notre exemple de cible, mais je ne discuterai pas comment le forcer, mais seulement comment coder le patcher. La section de code cible dans SuperCleaner.exe est a considéré dans Olly comme ce qui suit :

Nous devons patché cette ligne :

0042374F 75 41 JNZ SHORT SuperCle.004234C2

avec cette modif:

0042374F EB 41 JMP SHORT SuperCle.004234C2

Cela implique le changement seulement de 1 octet (75 à EB) à RVA 2347F (ImageBase de 400000 soustrait de VA pour obtenir RVA - voir mon tutoriel sur le PE pour plus de détail). Nous pourrions maintenant convertir le RVA de l'adresse du patch dans la mémoire à un offset dans le fichier sur le disque pour le patcher (de nouveau voir mon tutorial sur le PE) . Une méthode plus facile doit permettre de rechercher dans le fichier avec un éditeur Hexa pour trouver la séquence correct.

Autant que la recherche d'une séquence d'octets uniques est concernée, il y a 5 cas de 75 41 dans le fichier cible, mais seulement 1 cas de 75 41 E8 BA, donc nous utiliserons un DWORD pour notre séquence cible. Dans ce cas, comme les choses sont faciles, le RVA de l'adresse du patch est le même que l'offset sur le disque. Si l'offset attendue est trouvé, notre fichier cible doit être la version correcte.

Nous coderons une petite application(notre patch) à base de dialogue avec 3 boutons pour Apply(Appliquer), About(ou A propos de...) et Exit(Sortir ou Fermer). Nous utiliserons une case à cocher(CheckBox) pour créer une sauvegarde et une liste de choix(ListBox) à la production de notre succès ou des rapports d'erreurs, qui sont plus commodes pour l'utilisateur, que le besoin de cliquer sur OK sur de nombreuses MessageBox. Ouvrir WinAsm comme d'habitude et ajoutez un script de ressource. Faites-le comme cela :

La conception est inspirée par les patchs produits par dup2 (Diablo2oo2's Patcher Universel). J'ai utilisé un Logo (inclus dans ces archives) mais le patcher peut être entièrement personnalisé comme dans l'exemple keygen. En positionnant la case à cocher, vous devez traîner un rectangle assez grand pour l'étiquette de texte ainsi que la case à cocher elle-même. Le script (patcher.rc) est inclus dans la section de code source.

Maintenant, coller le code de offsetpatcher.asm dans WinAsm :

Remarquez au-dessus des 2 prototypes de fonction, pour notre fonction de patch et une petite routine pour mettre à jour notre liste. Remarquez aussi notre séquence d'octets cible, patch des octets et l'offset dans la section .data. Les séquences d'octets sont en réalité des tableaux - nous discuterons de ceux-ci dans le chapitre Structure des Données.

NOTE: Des nombres hexadécimaux doivent toujours commencer par un chiffre (0-9) décimal. Si nécessaire, ajoutez un zéro principal pour distinguer entre des symboles et des nombres hexadécimaux plutôt que le début avec une lettre.

Nous avons aussi le nom de notre cible et le nom que nous utiliserons pour le fichier de sauvegarde. Dans la section du .data ? nous avons des buffers pour lire dans les octets à l' offset cible et aussi prendre le nombre d'octets lus et écrits par les Fonctions de WriteFile et de ReadFile. La procédure de Dialogue devrait être surtout évidente, mais contient des appels à nos 2 Listes de fonctions et Patch :

La fonction de Liste prend un indicateur sur la String à être imprimé dans la liste et envoie des messages à la Liste(ListBox) pour ajouter(add) la String et aussi faire défiler pour garder le nouveau message en vue :

Les fonctions de démarrage du Patch vérifiées les attributs du fichier cible pour assurer qu' ils ne sont pas en lecture seule (FILE_ATTRIBUTE_READONLY). Si ils sont en lecture seiule, alors l'instruction suivante les fait mettre en Lecture-Ecriture ( FILE_ATTRIBUTE_NORMAL) pour permettre au fichier d'être écrit.

Ensuite nous essayons d'ouvrir le fichier avec CreateFile. Si le fichier est déjà utilisé (ouvert) nous obtenons une erreur de handle invalide, donc nous l'annonçons et Exit(Sortir ou Fermer). Si couronné de succès (pas d'erreurs) nous regardons la case à cocher. Si vérifié nous copions le fichier et le rebaptisons à notre nom de secours et annonçons la sauvegarde couronnée de succès dans la liste(ListBox).

Finalement nous utilisons SetFilePointer avec l'offset du patch et lisons 4 octets pour voir si nous avons la version cible correcte, la version incorrecte ou un fichier déjà patché. Si nous avons la version de fichier correcte nous retardons l'indicateur de fichier sur le début de l'offset du patch de nouveau et utilisons WriteFile pour patcher notre octet cible, rapport erreurs ou le succès dans la liste(ListBox) avant le retour.

NOTE: Le caractère d'antislash peut être utilisé pour recouvrir de longs arguments pour faire fonctionner plus d'appels de plusieurs lignes. Ici l'appel à CreateFile a un argument par ligne.

Ces routines ordinaires sont bien mais ... et si une nouvelle version du fichier cible est sortie dans lequel la protection est exactement la même mais les offsets ont changés légèrement ? Dans le tutorial suivant, nous améliorerons notre patcher en ajoutant la fonctionnalité pour parcourir le fichier pour la séquence d'octets cible à un offset différent pour permettre de créer un patch couronnée de succès. De plus nous regarderons la mémoire dressant la carte d'un fichier qui est une alternative beaucoup plus lisse à l'utilisation de l'indicateur de fichier.


ARTeam Tutorial

Patcheur Cherche & Remplace

Nouveau Concepts:

Fichiers planifiant la mémoire(RAM)
Instructions de string en assembleur
Utilisation de variables locales dans vos procédures
Retour de codes avec succès/erreur de codes

Nouvelles fonctions API:

CreateFileMapping
MapViewOfFile
UnmapViewOfFile
CloseMapFile

Dans ce tutorial nous rendrons notre exemple précédent plus puissant, en y ajoutant une fonctionnalité pour pouvoir ouvrir un fichier et pour que l'ordre des octets restent corrects, de façon à ce que les changements mineurs apportés à la version du programme par le patch n'affectent pas la routine de protection. D'abord, Nous devrons encore recourir à une certaine théorie.

Fichiers planifiant la mémoire(RAM)

Le File Mapping (Configuration d'un fichier) est une manière de regarder le contenu d'un fichier sans devoir l'assigner à un grand bloc mémoire et sans le charger entièrement. Ce fichier peut être n'importe quel fichier sur le disque que vous pourrez tracer dans la mémoire ou il peut être aussi un fichier d'un dossier système.

La première étape est de créer un fichier qui peut se composer de l'ensemble ou d'une partie d'un fichier. Il est placé dans un dossier sur le disque. Ceci signifie que quand le système permutent hors de son dossier le fichier, tous les changements faits au fichier sont écrits dans le dossier sur le disque. Quand le fichier est permuté dans son dossier il est reconstitué à partir du dossier.

La taille du fichier est indépendante de la taille du fichier de destination. Si le fichier source est plus petit que le fichier de destination, le système se préoccupera seulement du nombre indiqué d'Octets à partir du fichier. De Créer un fichier, cela ne prend pas de la mémoire physique (RAM), cela la réserve seulement. Il est donc d'usage d'en créer un de la même taille que le fichier même si vous ne comptez pas regarder le fichier entier ainsi ça n'engendrera aucun débordement dans les ressource du système.

Cependant, si le fichier est plus grand que le dossier, le système augmente le dossier en apposant le remplissage nulle avant que CreateFileMapping retourne. Les fichier sont statiques dans la taille ; une fois qu'ils sont créés, leur taille ne peut ni augmenter ni diminuer.

La prochaine étape sera pour tracer une vue de dossier (view file) de cet objet dans l'espace adresse (mémoire virtuelle) de votre Application. Une vue de dossier peut se composer de tout ou seulement d'une partie de dossier traçant l'objet, mais ne peut pas être plus grande. La fonction de MapViewOfFile renvoie un pointeur à la vue de dossier (view file). en dé référenciant le pointeur de l'application, on peut lire des données à partir du dossier et écrire des données au dossier.

L'écriture de la vue de dossier (view file) donne les résultats dans les changements au dossier traçant l'objet. L'écriture du dossier sur le disque est traitée par le système. Les données ne sont pas réellement transférées alors le plan du dossier de l'objet est écrit. Au lieu de cela, une grande partie de l'entrée de dossier et l'action (entrée-sortie) sont cachés pour améliorer l'exécution du système général. Cela peut être ignoré par la fonction de FlushViewOfFile qui copie le nombre indiqué d'octets de la vue de fichier(view file) au fichier physique immédiatement, sans attendre ceux qui sont cachés, pour arriver à écrire l'opération.

Quand un processus a fini avec le fichier dressant le plan de l'objet, il doit détruire toutes les vues de fichier dans son espace d'adresse en utilisant la fonction UnmapViewOfFile pour chaque vue de fichier. Si n'importe laquelle des pages de la vue de fichier a changé, depuis que la vue du plan a été dressée, le système écrit les pages changées du fichier au disque utilisant caching.

La configuration de fichier fournit plusieurs avantages:

1. Plus rapide et accès du fichier plus facile.
2. Seulement la partie nécessaire d'un fichier doit être planifier dans la mémoire
3. Mémoire Partagée entre deux ou plusieurs applications.
4. Aucun besoin d'utiliser les fonctions d'API de la mémoire et des fonctions d'API séparées d'entrée-sortie du Fichier.

La configuration du fichier permet à un processus d'avoir accès aux fichiers plus rapidement et facilement en utilisant un indicateur sur une vue de fichier. L'utilisation d'un indicateur améliore l'efficacité parce que le fichier réside sur le disque, mais la vue de fichier réside dans la mémoire.

Il permet aussi au processus d'efficacement fonctionner avec un grand fichier de données, comme une base de données, sans devoir dresser le plan du fichier entier dans la mémoire . Quand le processus a besoin de données d'une partie du fichier autre que ce qui est dans la vue de fichier actuelle, il peut ne pas dresser le plan de la vue de fichier actuelle, dressant alors le plan dans une nouvelle vue. La configuration du fichier est aussi utilisée comme le moyen de partager.

Le chargeur(loader) de Windows utilise la configuration de fichier pour charger des fichiers de Pe dans la mémoire. C'est très commode depuis seulement les parties nécessaires, qui peuvent être lues sélectivement du fichier sur le disque. Sous Win32, vous devriez utiliser le fichier dressant le plan autant que possible.

L'interface utilisateur est exactement le même que le tutorial si on réutilise juste le même script de ressource. Collez le code de SnRpatcher.asm dans WinAsm:

La procédure de Liste est exactement la même que la dernière fois et la procédure du patch est très semblable avec une certaine disposition faite pour appeler la procédure de Feuilletage si l'ordre d'octets cible n'est pas trouvé à l'adresse attendue :

Le procédé de balayage trace simplement dans une vue du dossier et l'ordre du balayage octet par octet sont très intéressant. Quand un match est trouvé pour le premier octet, l'octet suivant est comparé à notre ordre de cible. Si c'est un match, alors que l'octet suivant est comparé mais que le deuxième octet n'est pas un match, le balayage continue. Une variable locale est employée pour stocker les résultats retournés par la routine dans eax. Ces questions sont discutées plus en profondeur ci-dessous :

Variables Locales

Des variables locales sont définies avec le nom suivant, LOCAL de syntaxe : la taille which doit être sur la ligne juste après le rapport de proc. À la différence des variables globales que nous sommes habitués à employer, elles ne peuvent pas être initialisées en tant qu'élément de définition. Dans l'exemple ci-dessus une instruction de mouvements est utilisée comme moyen d'initialiser le flag qui sera mis à 0. employer des variables locales a l'avantage qu'elles peuvent seulement être vu par le procédé, qu'elles sont dans le procédé et sont détruites quand le procédé retourne.

Il est également facile de les utiliser en tant que flag de commande pour que MASM obtienne un bon empilage. Le chargement de l'adresse des variables locales doit être fait différemment des globals. Puisque l'adresse est une valeur sans aucune signification réelle sur la structure de l'empilage provisoire (pourquoi provisoire me direz-vous ? et bien par ce qu'en ASM , la pile (Stack) est utilisée pour empiler et dépiler des valeurs dans les registres comme eAX,eCX etc...),l'opérateur de cette valeur ne peut pas renvoyez l'adresse donc l'instruction lea doit être utilisée à la place.

Instructions de String Assembleur

Il y a 5 instructions de string pour le traitement rapide et efficace des strings entières et de rangées. Le terme "instructions de string" se rapporte à un ordre de tous les éléments, non pas des chaînes de caractères simplement. Ces instructions travaillent directement sur toutes les rangées d'octets, de mots, et de dwords. Tous ont la syntaxe semblable :

MOVS - Pour copier et déplacer en rangeant des données d'un secteur de mémoire à un autre .
SCAS - pour chercher ("parcourir") en rangeant des données en mémoire et en les comparants à la valeur dans eax.
CMPS - pour comparer des données de tableau et indiquer l'adresse correspondante après un non (négation).
STOS - pour remplir des données de tableau par "Stockage" de la valeur dans eax dans chaque élément.
LODS - pour charger des données de tableau dans eax.

Chaque instruction de string fonctionne à la fois sur un élément de tableau et a des formules mnémoniques finissant avec b, w, ou d (l'octet, le mot ou dword) pour spécifier la taille de l'élément de string à être exploité. Ces instructions de string exigent souvent trois registres pour des opérandes :

ESI - s'approvisionnent en adresse de données de tableau
EDI - l'adresse de destination de données de tableau
ECX - le compte d'élément (facultatif)

Ils dépendent aussi de la valeur du flag de direction dans les flags enregistrés et peut utiliser tout ou une partie d'EAX.

Les incréments d'instruction ou les décroissances SI et DI après un élément de string, ont l'accès pour qu'ils l'indiquent à l'élément de tableau suivant. Le flag de direction détermine si SI et DI sont incrémentés (le flag clair, 0) ou décrémentés (le jeu de flag, 1). La taille de l'instruction détermine si SI et DI sont changés par 1, 2, ou 4 octets.

Tous, sauf LODS, peuvent être utilisés avec les préfixes d'instruction de répétition (LODS n'est pas utilisé avec un préfixe de répétition, depuis continuellement, le rechargement EAX avec la même valeur ne sert à aucun but). Le préfixe de répétition cause l'instruction qui le suit pour se répéter automatiquement pour le nombre de temps indiqués dans ECX ou jusqu'à ce qu'une condition devienne vraie:

REP - Instruction de répétitions de temps CX
REPE (REPZ) - Le maximum d'instruction de répétitions de temps CX tandis que les valeurs sont égales
REPNE (REPNZ) - Maximum d'instruction de répétitions de temps CX tandis que valeurs non égales

Dans le susdit, exemple utilisant repnz scasb, l'adresse de notre ordre d'octets cible est chargée dans ESI, l'adresse de la vue de fichier est chargée dans EDI et le nombre d'octets dans le fichier est chargé dans ECX. Ensuite le premier octet de notre ordre cible est copié dans EAX. C'est parce que SCAS fonctionne en parcourant par EDI la recherche de la valeur dans EAX. repnz scasb continue à parcourir la boucle par EDI tant que la valeur du compteur n'est pas atteinte (la RÉPÉTITION bien que n'étant pas le Zéro ou Non Égal) ou jusqu'à ce que ECX frappe le zéro (la fin du fichier) pour que la routine s'arrête. Dés que la valeur du compteur est atteinte, il arrêtera de parcourir la boucle et l'état actuel (ESI, EDI et ECX) est sauvé sur la pile et la partie comparante des démarrages ordinaires.

EDI est diminué de 1 (comme instruction de scas arrêtée après la valeur du compteur), ECX est placé (à la longueur 4 de notre ordre de cible) et ESI contient toujours l'adresse de notre ordre d'octet de cible. Le cmpsb de Repz compare des octets successifs dans ESI et EDI jusqu'à ce qu'ils soient identiques (la comparaison se répéte jusqu'à ce que ce soit à zéro ou égal) et des arrêts s'il y a une disparité. Si ECX a réduit à 0 quand les CMPS s'arrête, ça signifie que chacun des 4 octets a été assorti et la routine stoppe. Si ECX>0 (IE moins de 4 octets assortis) alors l'état précédent est sauté outre de la pile et la routine de balayage reprend. L'action précise que chacune des instructions de string doit être illustrée par des exemples ainsi je recommande d'obtenir un des ebooks de la page de référence à la fin de ce cours d'instruction et de le lire du début jusqu'à la fin correctement. Les limitations de la conception ce Patcher sont très simples. Il y a seulement 1 octet à raccorder et nous avons pris le temps de s'assurer que notre ordre d'octet de cible seulement s'est produit par le passé. Plus de code serait nécessaire si les patchs multiples rapportées étaient nécessaires ou si l'ordre d'octet se produisait plus d'une fois, mais seulement un exemple avait besoin d'être raccorder. Diablo2oo2, qui est l'auteur du plus excellent des patchers (dup2) a édité le sourcecode pour la recherche et remplace le moteur sur son site Web. C'est une très belle routine très et mérite un regard. J'ai inclus un instantané de lui ici pour la référence rapide mais elle peut être téléchargée dans un fichier ZIP ici :

http://diablo2oo2.di.funpic.de/downloads/dup.search.and.replace.patchengine.sourcecode.rar


ARTeam Tutorial

Structures de données

Nouveaux Concepts:

Stockage de données
Variables
Indicateurs
Caractère indirect & Non référencer(Dereferenced)
Tableaux, Structures & Unions

Avant d'aller plus loin, il est nécessaire de comprendre les structures de données comme ceux-ci peuvent sembler intimidant, mais sont importants dans la programmation de Win32 et le reversing. Par exemple les données qui composent les en-têtes de fichiers exécutables portent ses contenues dans des structures diverses, si pour coder n'importe quel application qui manipule le PE des fichiers, vous devez savoir comment les structures de données sont traitées dans masm. SEH (Structured Exception Handling = le Traitement d'Exception Structuré) qui figure dans quelques systèmes de protection, utilise aussi des structures. Nous avons rencontré par hasard le startupinfo et les structures de process_information dans le dernier Tutorial. Dans le suivant nous utiliserons la structure d'openfilename et travaillerons avec des en-têtes PE.

Stockage de Données en Mémoire

Bien qu'une unité de mémoire dans des ordinateurs modernes tienne un octet, les unités de données utilisées dans un programme sont souvent beaucoup plus grandes. Avant que n'importe quelles données ne peuvent être stockées dans la mémoire, vous devez dire à l'ordinateur combien d'espace il faut réserver en utilisant des données abstraites types. Cela spécifie que la quantité de mémoire a eu besoin de stocker des données et fait en sorte que des données y seront stockées dans cet emplacement de mémoire. Il y a 2 types de types de données abstraits; Des types de données primitives et des types de données définis par l'utilisateur.

Des types de données primitives sont créées dans un language de programmation. Par exemple, masm utilise les types suivants, le nombre entier et la variable :

BYTE 8 bits Nombres non signés de 0 à 255
WORD (2 bytes) 16 bits Nombres non signés de 0 à 65,535 (64Kb)
DWORD (4 bytes) 32 bits Nombres non signés de 0 à 4,294,967,295 (4Mb)
SWORD (Word signé) Nombres signés de -32,768 à +32,767
SDWORD (DoubleWord signé) Nombres signés de -2Mb à +2Mb

Des types de données définis par l'utilisateur sont les groupes de types de données primitives définis par le programmeur et incluent des String, des tableaux, des structures et des unions dans lesquelles vous pouvez avoir accès aux données comme une unité ou comme les éléments individuels qui composent l'unité.

Tandis que les variables peuvent se terminer et être placé n'importe où dans la mémoire, les éléments dans des types de données définis par l'utilisateur sont l'un à côté de l'autre dans la mémoire. Il est plus efficace d'indiquer pour ranger des éléments que des variables parce que vous pouvez indiquer à l'élément de tableau suivant en vous déplaçant simplement à l'emplacement de mémoire suivant. Nous discuterons des tableaux et des structures plus en détail bientôt.

Caractère indirect et Non référencer(Dereferenced)

Il est important de comprendre la distinction entre le CONTENU d'une variable (sa valeur) et l'ADRESSE d'une variable (son emplacement dans la mémoire).

Quand vous vous référez à une variable vous faites la référence DIRECTE, mais quand vous vous référez à l'ADRESSE d'une variable, vous faites la référence INDIRECTE.

L'ayant de l'ADRESSE d'une variable, établit "un" niveau de CARACTÈRE INDIRECT. Quand vous traitez avec des tableaux vous pouvez avoir un tableau d'adresses. Si vous utilisez l'ADRESSE de ce tableau pour avoir accès à la VALEUR d'une variable et de son adresse dans le tableau vous avez un niveau supplémentaire de caractère indirect.

La raison de pourquoi les niveaux de caractère indirect sont si utiles, est qu'une adresse de DWORD simple ou un INDICATEUR peuvent avoir accès à un beaucoup plus grand nombre de variables très rapidement.

NOTE: Un indicateur est simplement une variable qui contient une adresse comme sa valeur.

Il est possible d'enlever un niveau de caractère indirect d'une adresse en l'incluant dans des parenthèses carrées. Cette technique est appelée SANS REFERANCE(DEREFERENCING). Dans l'exemple suivant eax est utilisé comme un indicateur sur la variable MyVar et est sans référence(dereferencing) dans ebx pour donner la valeur de la variable :

.data
MyVar dd 77h

.code
mov eax,offset MyVar ; eax contient maintenant l'adresse de MyVar
mov ebx,[eax] ; ebx contient maintenant 77h

Voir Ici Pour les explications à propos de [] syntaxe.

TABLEAUX

Comme dit ci-dessus, un tableau est une collection séquentielle de variables, toutes de la même taille et de même type, appelées des éléments. Vous pouvez avoir accès à tous les éléments dans un tableau autant qu'au premier élément. Tandis qu'un tableau est identifié d'un nom, chaque élément dans un tableau en fait référence avec un nombre d'indice, commençant par le zéro. L'indice de tableau apparaît entre parenthèses carrées après le nom :

Tableau[9]

Dans l'assembleur l'indice d'un élément se réfère au nombre d'octets entre l'élément et le début du tableau. Cette distinction peut être ignorée pour les tableaux d'éléments en taille d'octet, puisque le nombre de position d'un élément correspond à son indice. Par exemple, définissant un tableau d'octets a appelé "prime" :

prime Octet(byte) 1, 3, 5, 7, 11, 13, 17

Donne une valeur de 1 à prime [0], une valeur de 3 à prime [1], et ainsi de suite. Cependant, dans des tableaux avec des éléments plus grand que 1 octet, les nombres d'indice (sauf le zéro) ne correspondent pas à la position d'un élément. Vous devez multiplier la position d'un élément de sa taille pour déterminer l'indice de l'élément. Ainsi, pour le tableau

wprime WORD 1, 3, 5, 7, 11, 13, 17

wprime [4] représente le troisième élément (5), qui est de 4 octets depuis le début du tableau. De même l'expression wprime [6] représente le quatrième élément (7) et wprime [10] représente le sixième élément (13).

L'offset exigé pour avoir accès à un élément de tableau est calculé avec la formule suivante: nth element of array = array[(n-1) * size of element]
(Le énième élément de tableau = range [(n-1) * par la taille de l'élément])

Un String est aussi un tableau de caractères qui prend 1 octet de mémoire.

STRUCTURES

Les structures sont simplement des modèles pour des données à être stocker en mémoire d'une façon organisée. Les structures ne sont pas des données.

Une structure est un groupe de types de données abstraits qui peuvent être accessible comme une unité ou par n'importe lequel de ses champs de composants. Les champs dans la structure peuvent être des types de données différentes et de tailles différentes. Ils sont placés séquentiellement dans la mémoire quand un cas de la structure est défini dans un programme.

Vous pouvez utiliser la variable de la structure entière ou juste les champs individuels comme des opérandes dans des déclarations d'assembleur. En utilisant des structures, on doit déclarer le type de structure d'abord. Quand vous déclarez une structure ou un type d'union, vous créez un modèle pour des données. Le modèle expose les tailles et, facultativement, les valeurs initiales dans la structure, mais n'alloue aucune mémoire.

Une fois que vous avez déclaré un type de structure, vous pouvez définir n'importe quel nombre de variables de ce type. Pour chaque variable définie, la mémoire est allouée. Les champs individuels peuvent alors faire référence directement ou indirectement. Comme d'autres variables, les variables de structure peuvent avoir un accès du nom. Vous pouvez directement avoir accès aux champs dans des variables de structure avec cette syntaxe : variable.field

1. Déclarez d'abord le modèle de structure. C'est fait d'habitude avec des déclarations de prototype après les déclarations avec include et includelib :

DATE STRUCT
day BYTE ?
month BYTE ?
year WORD ?
DATE ENDS

2. Définissez ensuite la variable (un cas de la structure de DATE appelée "yesterday") l'un ou l'autre avec des données non initialisées :

.data?
yesterday DATE <>

Ou avec des données initialisées :

.data
yesterday DATE <19, 06, 1993>

3. Finalement vous pouvez avoir accès à la structure et ses champs directement du nom comme cela :

mov al, yesterday.month

Ou indirectement via un indicateur comme cela :

mov ebx, offset yesterday ;Adresse de chargement de la variable yesterday dans ebx
mov al, [ebx].DATE.month ;Copie du champ month d'ebx dans Al

L'utilisation d'un indicateur DWORD sur une structure accélère des appels de fonction puisque les grandes quantités de données dans une structure peuvent être appelées par la fonction mais seulement l'indicateur doit être mis dans la pile.

Les structures peuvent aussi être emboîtées. Les éléments de structures emboîtées ont accès de la même façon comme indiqué ci-dessus. Un exemple du format de PE est l'en-tête PE:

Ceci contient 3 éléments, tout d'abord DWORD dont la tenue de la signature de l'exécutable est non initialisé (pour PE, NE, OS/2, etc..) alors que les 2 autres sont emboîtés plus loin dans des structures tenant l'en-tête de fichier et l'en-tête facultatif. L'en-tête de fichier est comme suit :

Ainsi la Signature et les champs NumberOfSections peuvent avoir accès indirectement tout à fait simplement. Nous avons un indicateur sur le début de l'en-tête PE dans edi, alors vous pouvez super imposer le modèle de structure sur ce bloc de mémoire pour adresser les champs divers de nom comme cela :

mov eax,[edi].IMAGE_NT_HEADERS.Signature
mov bx,[edi].IMAGE_NT_HEADERS.FileHeader.NumberOfSections

Masm le comprendra aussi si les parenthèses carrées incluent l'opérande entier :

mov eax,[edi.IMAGE_NT_HEADERS.Signature]
mov bx,[edi.IMAGE_NT_HEADERS.FileHeader.NumberOfSections]

Nous en verrons plus dans le Tutorial suivant. Il y a aussi une autre façon d'utiliser des structures indirectement avec une directive qui assume. Cela dit, à l'assembleur de substituer le registre de votre choix pour une expression contenant votre indicateur de structure. Par exemple :

assume edi:ptr IMAGE_NT_HEADERS
mov eax,[edi].Signature
mov bx,[edi].FileHeader.NumberOfSections
assume edi:nothing

Cependant pendant que cela apparaît légèrement plus rapide, en assumant un registre oubliant alors de non supposer qu'il produit des résultats imprévisibles. Aussi la directive qui assume, pourrait être jusqu'ici une ligne qui a besoin que la signification soit laissée peu claire.

Il y a une quatrième syntaxe pour le faire mais c'est maladroit et ne marchera pas dans certaines circonstances (eg après une directive .if) donc il est recommandé d'utiliser la première méthode. Il utilise l'opérateur ptr et je l'ai inclus ici pour la perfection :

mov eax,(IMAGE_NT_HEADERS ptr [edi]).Signature
mov bx,(IMAGE_NT_HEADERS ptr [eax]).FileHeader.NumberOfSections

Cette section deviendra particulièrement importante quand nous parlons du codage pour des fichiers de PE plus tard.

UNIONS

Les unions arrivent aussi dans les en-têtes PE. Ils sont identiques aux structures, sauf que les champs d'une union se chevauchent dans la mémoire, qui admet que vous formatiez pour définir des données différentes pour le même espace de mémoire. Les unions peuvent stocker les types différents de données selon la situation. Ils peuvent aussi stocker des données comme une donnée type et le récupère comme un autre type de données.

Tandis que chaque champ dans une structure a un offset quant au premier octet de la structure, tous les champs dans un début d'union à le même offset. La taille d'une structure est la somme de ses composants; la taille d'une union est la longueur du champ le plus long. Un exemple d'une union dans le format de PE est le premier membre de la structure de IMAGE_IMPORT_DESCRIPTOR :

Le premier membre peut ainsi contenir des Caractéristiques ou des données OriginalFirstThunk. Dans ce cas les Caractéristiques peuvent une fois avoir été un jeu de flags mais cela a été cessé par M$ et il représente maintenant toujours les données OriginalFirstThunk.

À propos, rendez-vous compte que l'on déclare déjà la plupart des structures nécessaires pour coder dans des fenêtres (incluant PE stuff)) pour nous dans le fichier de windows.inc.


ARTeam Tutorial

Chargeur de Base(Loader)

Nouveaux Concepts:

Processus et Threads
Gestion de Mémoire Virtuelle

Nouvelles fonctions API:

CreateProcess
WriteProcessMemory
ResumeThread
TerminateProcess

Un chargeur(loader) est un programme autonome qui a pour habitude de charger et exécuter un autre programme. Il est utilisé quand nous voulons patcher une cible dans la mémoire, par exemple après qu'un contrôle de CRC soit fait, ou quand nous voulons changer quelque chose et plus tard dans le programme rétablir les octets originaux. Un exemple d'un chargeur qui est un "trainer" pour pouvoir tricher dans certains jeux.

Dans ce Tutorial nous coderons un chargeur(loader) très simple et comparerons son action au patcher que nous avons fait dans le dernier Tutorial. D'abord nous devons recourir à une certaine théorie générale sur des processus, des Threads et la gestion de mémoire.

Processus et Threads

Un processus fournit les ressources dont à besoin un programme pour être exécuter. Chaque processus est une collection d'adresse virtuelle (la mémoire), un espace du code de l'exécutable, des données, des handles ouverts aux objets du système, un contexte de sécurité, un identificateur de processus unique, des variables d'environnement, une priorité de base, le minimum et le maximum travaillant des jeux de tailles et au moins un Thread d'exécution.

Un Thread est le code qui doit être exécuté dans un processus. Un processeur exécute des Threads, pas des processus, donc chaque application a au moins un processus et un processus a toujours une direction de Thread, connu comme le Thread principal(ou primaire). Un processus peut aussi avoir des Threads multiples.

Tous les Threads d'un processus partagent son adresse virtuelle, des espaces de variables globales et des ressources. De plus, chaque Thread maintient des handles d'exception, une priorité de planification, le Thread de stockage local (eg des variables locales), un identificateur de Thread unique et un jeu de structures que le système utilisera pour sauver le contexte du Thread jusqu'à ce qu'il soit prévu. Le contexte du Thread inclut le jeu du Thread de registres de la machine, la pile du kernel, un bloc d'environnement de Thread et une pile d'utilisateur dans l'adresse espace du processus du Thread.

L'utilisation de Threads donnent leur capacité de multi gestion apparente à Windows - la capacité d'exécuter des programmes multiples concurremment. Au lieu de continuer pour exécuter un seul morceau de code jusqu'à ce qu'il soit complété, windows peut décider d'interrompre un Thread fonctionnant à n'importe quel moment et le commuter à un autre Thread.

Un Thread ressemble à un petit processeur virtuel qui a son contexte propre et sa pile propre. Les commutateurs de processeurs physiques réels entre des processeurs virtuels multiples, commencent toujours l'exécution des informations de contexte actuelles du Threads et l'utilisation de la pile du Thread.

Les composants qui gèrent des Threads dans Windows sont le planificateur et le dispatcher, qui sont ensembles responsables à se décider auquel combien de temps le Thread obtient pour poser sa condition et pour exécuter le contexte réel qui commute son temps pour changer de Threads.

Les Threads peuvent céder à l'UC de leur propre volonté, par exemple s'ils sont inoccupés en attendant que l'utilisateur saisisse. Cependant l'OS utilise un système appelé, par préemption, planification dans le cas où un Thread commence une longue tâche qui occuperait l'UC pour quelque temps. On donne un temps limité aux Thread (le quantum ou la tranche de temps) pour fonctionner avant qu'ils ne soient interrompus.

Tandis qu'un Thread fonctionne, l'OS utilise un minuteur de matériel à bas niveau interrompant pour contrôler combien de temps il a fonctionné. Une fois que le quantum du Thread a atteint son but, il est temporairement interrompu et le système permet à d'autres Threads de fonctionner. Si aucun autre Thread n'a besoin de l'UC, le Thread est immédiatement repris. Le processus de suspension et la reprise du Thread est complètement transparent au Thread. Le kernel stocke l'état de tous les registres de l'UC dans une structure de CONTEXTE avant la suspension du Thread et rétablit cet état quand le Thread est repris. De ce fait, le Thread crois qu'il n'a jamais été interrompu.

L'Ordre d'Initialisation du Processus

Il est important d'avoir une bonne compréhension de ce qui arrive quand un processus est créé. La chose suivante fournit une description brève des étapes pris par le système dans le processus moyen de l'ordre d'initialisation.

1. L'API CreateProcess crée un nouvel objet de processus et alloue une adresse espace virtuel (voir le paragraphe suivant).
 2. CreateProcess dresse alors le plan de NTDLL.DLL et le programme exécutable (le fichier .exe) dans l'adresse espace nouvellement alloué.
 3. CreateProcess crée le premier thread du processus et alloue une adresse espace de pile.
 4. Le premier thread du processus commence à fonctionner avec la fonction LdrpInitialize à l'intérieur de NTDLL.DLL.
 5. LdrpInitialize traverse récursivement les tables d'importation de l'exécutable et des plans dans la mémoire qui sont éxigés pour chaque exécutable.
 6. À ce point, LdrpRunInitializeRoutines est appelé et est responsable d'initialiser toutes les DLLS statiquement, lié actuellement et chargées dans l'espace d'adresse. Le processus d'initialisation consiste à en appeler le point d'entrée de chaque DLL avec le DLL_PROCESS_ATTACH constant voir le Code Squelettique d'une DLL.
).
7. Une fois que toutes les DLL sont initialisées, LdrpInitialize appelle la routine d'initialisation réelle du thread, qui est la fonction de BaseProcessStart de KERNEL32. DLL. Cette fonction appelle à son tour le point d'entrée WinMain de l'exécutable, auquel le point du processus a complété son ordre d'initialisation.

Fenêtres de Gestion Mémoire Virtuelle

Chaque processus a son espace d'adresse virtuel propre qui permet l'adressage jusqu'à 4 Gb de mémoire. Tous les threads d'un processus peuvent avoir accès à leur espace d'adresse virtuel, mais pas à la mémoire qui appartient à un autre processus, qui protège un processus d'être corrompu par un autre.

Des utilisations d'adresses virtuelles par un processus ne représentent pas l'emplacement physique réel d'un objet dans la mémoire. Au lieu de cela le système garde un plan de page pour chaque processus - une structure de données interne a pour habitude de traduire des adresses virtuelles dans la transmission d'adresses physiques. Chaque fois un thread fait référence à une adresse, le système traduit l'adresse virtuelle à une adresse physique. Les pages de l'adresse espace virtuel d'un processus peuvent être dans un des états suivants:

Free - La page est ni remise, ni réservée. La page n'est pas accessible au processus. Elle est disponible d'être remise, réservée, ou réservée simultanément et remise. La tentative de lire ou d'écrire dans une page libre(free) aboutit à une exception de violation d'accès.

 Reserved - La page a été réservée pour l'utilisation future. La gamme d'adresses ne peut pas être utilisée par d'autres fonctions d'assignation. La page n'est pas accessible et il n'y a aucun stockage physique associé. Elle est disponible d'être remise.

 Committed - Le stockage physique est alloué pour la page et l'accès est contrôlé par une option de protection de mémoire. Le système initialise et charge chaque page engagée dans la mémoire physique seulement pendant la première tentative de lecture ou d'écriture à cette page. Quand le processus se termine, le système sort le stockage pour des pages engagées.

La fonction de l'API VirtualAlloc est utilisée pour réserver et remettre en mémoire et spécifier des autorisations d'accès de mémoire pour des pages engagées :

Un processus utilise d'abord VirtualAlloc pour réserver un bloc de pages dans son adresse espace. Plus tard, VirtualAlloc est appelé de nouveau chaque fois qu'il est nécessaire de remettre une page de cette région réservée. La remise de la région entière au lieu de seulement le réserver consomme pour rien du stockage physique qui n'est pas nécessaire car le rendant indisponible pour l'utilisation par d'autres processus.

VirtualAllocEx est une fonction importante qui peut réserver ou remettre une région en mémoire dans l'adresse espace virtuel d'un autre processus indiqué.

Considérations en écrivant un Chargeur(Loader)

Un chargeur(loader) doit créer un nouveau processus et charger la cible, mais nous voulons faire une pause sur le thread aussitôt qu'il soit créé, ainsi nous pouvons changer les choses que nous voulons. Nous utilisons l'API CreateProcess pour cela avec la valeur de CREATE_SUSPENDED dans dwCreationFlags.

CreateProcess prend des indicateurs sur 2 structures importantes; ProcessInformation et StartupInfo. La structure de ProcessInformation est remplie du handle du processus, du handle de thread et de processus/thread ID (ouvrir win32.hlp pour plus de renseignements).

NOTE: Utilisant le handle de processus, ça vous donne l'accès avec PROCESS_ALL_ACCESS (lit/écrit l'accès) au processus entier. En utilisant le handle de thread, ça pourra vous permettre d'écrire l'accès manuellement.

Quand la cible est chargée dans le mode suspendu nous pouvons laisser le thread exécuté avec ResumeThread et mettre en pause de nouveau avec SuspendThread. Ces API prennent le handle de hThread trouvé dans le LPPROCESS_INFORMATION struct. Finalement nous pouvons lire et écrire dans le processus utilisant ReadProcessMemory et WriteProcessMemory.

Notre chargeur(loader) créera simplement un processus et chargera le thread principal suspendu de SuperCleaner. Alors il patchera le fichier dans la mémoire, puis l'exécution de résumé du thread principal et quittera.

Lancez WinAsm comme d'habitude et le coller dans loader1.asm. Bien que ce chargeur(loader) n'ait pas de GUI comme tel, il est agréable si cela a au moins obtenu une icône. Ajoutez un script de ressource au projet et ajoutez juste une icône. Cela produit le script suivant :

Le code d'assembleur est court, mais bon. Notez que puisqu'il n'y a aucun GUI (aucun appel à DialogBoxParam) nous ne devons pas déranger l'obtention et la sauvegarde du handle de module. Aussi s'il y a une erreur nous devons terminer le processus cible suspendu avant la sortie :

Une critique possible de ce code est que nous avons spécifié le VA à être patché. Cela compte pour la cible étant chargée de préférence avec ImageBase dans le patch pour travailler. Une voie plus robuste serait de spécifier que le RVA obtient alors l'adresse de base de la cible et les ajoute pour faire le VA patché. Pour cette voie, si la cible a été chargée à une base de différentes adresses, le chargeur patcherait toujours les octets correctement, bien que cela s'applique évidemment principalement aux DLL. Une autre critique est que c'est un chargeur aveugle - il ne vérifie pas, mais nous discuterons un peu plus de ces concepts avancés dans le Tutorial suivant.


ARTeam Tutorial

En savoir plus sur les Chargeurs

Nouveaux Concepts:

Utilisation du thread context
Permissions aux Accès Mémoire
Débuggeur Loaders

Nouvelles fonctions API:

SusupendThread
GetThreadContext
SetThreadContext
VirtualProtectEx
IsBadStringPtr
IsBadCodePtr
IsBadReadPtr
IsBadWritePtr
VirtualQueryEx
WaitForInputIdle
EnumDesktopWindows
GetClassName
GetWindowText
OpenProcess
DebugActiveProcess
WaitForDebugEvent
DebugActiveProcessStop

Structure du thread CONTEXT

Comme nous l'avons vu dans le dernier tutorial, chaque thread maintient une structure de CONTEXTE qui inclut les registres, la pile(stack) Kernel, un bloc d'environnement thread la pile User. Nous pouvons lire et changer les valeurs de registre d'un thread fonctionnant dans un processus en utilisant les APIs GetThreadContext et SetThreadContext pour lire ou écrire dans la structure de CONTEXTE..

Cette structure data a des champs complexes et seulement certains sont utiles mais nous pouvons spécifier le champ par get/set ContextFlags. La valeur de ContextFlags spécifie quelles parties du contexte thread est recouvré. La documentation dessus est dans WINNT.H et non à l'intérieur de MSDN :

context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS.

L'essai get/set dans un contexte pour un thread executé donne des résultats imprévisibles. Utilisez la fonction de SuspendThread pour suspendre leThread.

Gestion d'Autorisations d'accès à la Mémoire

Une question importante en écrivant des loaders c'est qu'il doit avoir la permission à la lecture /écriture en mémoire. D'habitude en ayant accès aux sections exécutables d'un processus, ces sections sont déjà lisibles à l'affichage. Cependant si vous essayez d'écrire dans d'autres sections elles devraient alors seulement être lues, comme pour les ressources du programme, vous échouerez.

L'écriture d'un loader(chargeur) pour avoir accès aux pages en mémoire protégées implique ainsi la modification des autorisations d'accès de l'adresse patchée à PAGE_EXECUTE_READWRITE, pour la lecture et l'écriture du tout nous avons besoin de reconstituer la protection précédente. La fonction VirtualProtectEx nous permet de le faire. Les paramètres suivants ont de l'intérêt :

hProcess - Handle à processus cible - Doit avoir PROCESS_VM_OPERATION access mais habituellement l'API CreateProcess le fait pour nous.

lpAddress - Pointeur de l'adresse de base de la région des pages dont les autorisations d'accès doivent être changées. Toutes les pages dans la région indiquée doivent être dans la même région réservée allouée en l'appelant par VirtualAlloc ou la fonction VirtualAllocEx utilisant MEM_RESERVE. Les pages ne peuvent pas recouvrir les régions réservées adjacentes qui ont été allouées par des appels séparés à VirtualAlloc ou VirtualAllocEx utilisant MEM_RESERVE.

dwSize - La taille dans les octets de ce que nous voulons Patcher. Inclut toutes les pages Contenant ou plus d'octets dans la gamme du paramètre lpAddress à (lpAddress+dwSize). Cela signifie qu'une gamme de 2 octets évitant la limite de page provoquent aux attributs de protection des deux pages leurs changements.

flNewProtect - protection Mémoire - nous avons besoin de PAGE_EXECUTE_READWRITE.

LpflOldProtect - Pointe sur une variable qui reçoit la protection d'accès précédente de la première page dans la région indiquée de pages. Si ce paramètre est NUL ou ne pointe pas à une variable valable, la fonction échoue. Rappelez-vous que l'API échoue si vous mettez ce paramètre à NUL, donc nous aurons besoin d'une variable pour stocker cette valeur.

Le jeu d'API utilisées pour évaluer des droits en mémoire(évaluant si une adresse en mémoire pointée peut être lue ou écrit) inclut IsBadStringPtr, IsBadCodePtr, IsBadReadPtr, IsBadWritePtr. Tous sont semblables dans leur utilisation; juste avant l'utilisation ReadProcessMemory ou WriteProcessMemory utilise la fonction appropriée pour évaluer alors l'utilisation VirtualProtectEx pour placer si nécessaire.

Arrête le Pointer et Décide quand Patcher.

Dans des cas simples (comme dans le tutorial précèdent) nous pouvons être capables de patcher une cible dans la mémoire aussitôt que son processus est créé, mais avant les thread principales soient exécutés. Dans d'autres cas nous devons laisser la cible exécutée et l'arrêter à un point spécifique (eg après qu'une application packée a été unpackée en mémoire ou après qu'un contrôle(check CRC) a été fait. Notre loader devrait donc être capable d'arrêter la l'application à un point indiqué, de patcher le code et laisser ensuite l'application librement.
Les questions concernant la synchronisation incluent :

1. L'Attente jusqu'à arriver à l'emplacement en mémoire que vous voulez patcher est unpacké et non par la protection du packeur .

2. Le patching de l'emplacement mémoire s'effectue seulement après que le (check control) contrôle d'intégrité soit effectué.
3. Le patching de l'emplacement mémoire se fait avant que les instructions ne soient exécutées ou utilisées par la cible(application).

Il existe plusieurs façons (certaines plus puissantes que d'autres) d'arrêter l'inclusion cible :

1. L'utilisation de l'api sleep est pour l'attendre d'une durée d'ensemble pour une application packée et d'être unpackée en mémoire.

2. L'utilisation de l'API WaitForInputIdle est pour déterminer quand la cible a été exécutée, et a fini de s'initialiser et inoccupée (attend jusqu'à ce que le processus pointé attende la saisie de l'utilisateur en suspens de saisie, ou avant que l'intervalle de temps mort se soit écoulé).

3. La découverte quand la fenêtre principale de l'application a été montrée. Dans ce cas nous supposons que si la fenêtre de la l'application principale est déjà sur le bureau (visible ou pas) alors la l'application fonctionne et est prête à être patchée. Cela s'exécute en utilisant l'API EnumDesktopWindows. Cette fonction énumère toutes les fenêtres associées au bureau pointé. Il passe le handle de chaque fenêtre ouverte à une fonction de rappel de service définie par l'utilisateur. Autrement dit il invoque à plusieurs reprises notre fonction de rappel de service jusqu'à ce que la dernière fenêtre au plus haut niveau soit énumérée ou les retours de fonction de rappel de service False. Notre fonction de rappel de service(callback) appellerait simplement GetClassName et GetWindowText pour chaque fenêtre pour voir si le type de la fenêtre et le titre correspondent à celui de notre cible. La classe de fenêtre principale de la cible peut être trouvée facilement en utilisant n'importe lequel de plusieurs outils d'inspection de fenêtres comme Winspector.

4. L'utilisation de l'yAtEs trompe pour injecter un point d'arrêt dans le code à un emplacement désiré (qui doit d'abord être trouvé en mettant au point l'application). Cela implique la lecture et le stockage de 2 octets à notre point d'arrêt désiré, alors l'injection des 2 octets hexa EB FE (jmp-2) au même point d'interrompre l'exécution en créant une boucle sans fin. Nous devons alors continuellement choisir le contexte du tread (utilisation GetThreadContext) pour la valeur dans le registre d'EIP . EIP est le pointeur d'instruction et tient l'adresse de l'instruction suivante qui va être exécutée. Quand la valeur ici == notre arrêt adresse nous pouvons suspendre thread et faire notre pacthing. Une fois que nous avons fini de patcher nous reconstituons 2 octets recopiés par EBFE et reprenons l'exécution. Cela a l'avantage qu'il agisse comme "un point de contrôle", mais sans utiliser aucune API dans le débuggeur et donc sans avoir besoin de s'inquiéter de n'importe quel mécanisme d'anti-debug dans notre application. Évidemment il y a quelques suppositions avec un loader si simple :

• La victime est un simple thread
• Une fois que l'application est unpackée en mémoire il n'y a aucun contrôle d'intégrité(check control)
• La mémoire du processus cible peut être écrite
• le contexte de sécurité de la victime nous permet de procéder
• La victime n'a pas de complexe de protections anti-tampering

Débuggeur Loaders

Les chargeurs(loaders) standard décrits ci-dessus ont le grand avantage qu'ils sont simples et pas soumis aux tours de programme anti-degug, mais il y a de temps où c'est nécessaire pour le loader de déboguer l'application cible pour trouver un point d'arrêt. Un exemple typique c'est en faisant un loader pour Asprotect. Nous sommes probablement tous familiers avec l'utilisation de la DERNIÈRE méthode d'EXCEPTION pour parvenir à l'OEP mais comment faire si nous voulons que notre loader fasse la même chose ?

Dans ce cas nous utilisons l'API debug pour créer un loader pour le programme dans le débuggeur conduit d'événement simple qui peut répondre pour le déboguage. Les événements de déboguage incluent des exceptions rejetées par le packeur de la même manière nous les voyons dans Olly donc notre loader de programme de déboguage utilise en réalité SEH (le traitement d'exception structuré).

Je décrirai seulement et brièvement ce type de loader mais une description détaillée peut être trouvée dans la série de loader d'articles en cracking par Shub et TunderPwr.

De même qu'avec un loader standard nous essayons de constater qu'en un temps approprié il patch notre cible. Dans ce cas cependant notre point d'arrêt sera une exception produite par le packeur de la cible. Nous devons d'abord déboguer la cible (avec le jeu d'Olly pour ne PAS manipuler n'importe quelles exceptions). Quand nous arrivons à l'endroit correct (eg la dernière exception dans Asprotect) nous devons examiner l'exception aux caractéristiques uniques (eg l'instruction PUSH 0C ). Notre loader doit donc vérifier chaque exception que la cible produit jusqu'à ce qu'elle trouve le modèle unique.

Un loader de programme pour débuggeur peut créer un nouveau processus de debogging ou "attacher" à un existant avant l'attente des événements de mise au point. Dans le premier cas l'API CreateProcess est utilisée avec DEBUG_ONLY_THIS_PROCESS ou avec DEBUG_PROCESS (mettra aussi au point tous les processus d'enfant) dans dwCreationFlags.

Dans le deuxième cas OpenProcess est utilisé pour obtenir le PID ou l'identificateur de processus d'un processus existant et DebugActiveProcess est utilisé avec ce PID pour attacher le programme de mise au point au processus. Typiquement les programmes de mise au point ouvrent un processus avec le PROCESS_VM_READ et les flag de PROCESS_VM_WRITE pour lire et écrire en mémoire virtuelle du processus en utilisant ReadProcessMemory et WriteProcessMemory.

Le loader doit alors utiliser une boucle appropriée avec WaitForDebugEvent pour attendre la mise au point des événements de la cible. Chaque exception produite par la cible et a passé à notre loader ,le programme de mise au point par l'OS doit être examiné pour voir s'il correspond aux caractéristiques uniques, nous cherchons avant le parching en mémoire en utilisant WriteProcessMemory.

Finalement le loader peut ou pas passer le contrôle à la cible (le maintien d'attaché), détacher de la cible et le laisser(quitter) pour exécuter(diriger) librement ou fermer la cible. Le détachement semble la façon la plus propre et est accomplie avec DebugActiveProcessStop mais ce n'est pas disponible dans OS avant Windows XP.

La mémoire évaluant l'approche basée sur isBad*Ptr nous avons parlé des fonctions dans les derniers tutoriaux où le loader lance le processus cible par l'API CreateProcess. Cependant si vous avez utilisé OpenProcess avec déjà le processus de direction, vous devriez utiliser VirtualQueryEx au lieu de cela qui fournit des informations sur une gamme de pages dans l'espace d'adresse virtuel d'un processus indiqué.

Bien que l'addition de la mise au point de capacités apporte la flexibilité il ajoute aussi la complexité et la nécessité d'éviter les mécanismes d'anti-debuging divers utilisés par des protecteurs.

Comme exemple pratique nous packerons le soft SuperCleaner et essayerons notre loader du dernier tutorial. Pour faire dans la simplicité nous utiliserons upx v1.25. Commandline habituel "upx SuperCleaner.exe" produit une erreur, mais en tapant "upx SuperCleaner.exe - force" le travail :

Remarquez que notre cible a diminué de taille de 508Ko à 209Ko. Maintenant clairement notre patchers précédent ne marchera pas sur la cible packée et aucun loader ne le fera. Essayez le loader pour voir ce qui ce passe :

Notre chargeur(loader) crée le processus dans le mode suspendu et essaye immédiatement de patcher la mémoire mais l'adresse correcte n'existe pas encore comme la souche upx n'a pas décompressé la cible dans la mémoire. Quand le thread est repris, la cible est unpackée et exécutée mais notre patch n'a pas été appliqué ainsi nous obtenons le petit (le Nag).

Pour le résoudre nous coderons un loader utilisant une des techniques appropriées. L'attente de la saisie se faisant attendre ou la fenêtre principale n'évitera pas évidemment le Nag et essayant de supposer qu'un Sleep time sera trop imprécis donc nous utiliserons la méthode yAtEs.

Ouvrez d'abord la cible packée dans Olly et trouvez le saut vers l'OEP. Dans upx a packé des applications c'est un point idéal auquel il dépose ou patch l'application puisque nous ne devons nous inquiéter d'aucun CRC ou des contrôles(check) anti-tampering et la découverte de l'OEP est très facile. Nous faisons simplement défiler en bas à la fin du code pour trouver l'instruction POPAD suivie par un JMP vers L'OEP :

Dans mon cas l'OEP est 0042AADA comme vous pouvez le voir.Nous savons déjà quelle est l'adresse virtuelle à être patchée c'est 0042374F. Une bonne façon de le défaire serait d'utiliser la méthode de l'yAtEs's. Nous créons le processus dans le mode suspendu et remplaçons premiers 2 octets du saut à l'OEP (E9h E9h) par EBH FEh. Notez qu'en s'arrêtant à l'OEP lui-même n'est pas une option comme cela n'aura pas été encore unpacké dans la mémoire. Nous devons alors faire notre patching dans la mémoire, rétablissons le saut final vers OEP et reprenons le Thread. Nous améliorerons également notre loader par la vérification les bytes corrects à l'OEP et à l'endroit patché afin de vérifier la version correcte de dossier de cible.

Créez un nouveau projet dans WinASM et collez dans loader2.asm et ajoutez loader.rc de la section source :

Ce chargeur commence le processus pour notre cible et lit 2 bytes à partir de l'adresse que nous pensons être le saut vers l'OEP. Si les bytes sont corrects (dossier de cible correct), il les recouvre avec le code d'arrêt d'EBFE avant de reprendre le Thread primaire et la pause pendant 10 millisecondes pour permettre à la cible de fonctionner. La cible s'exécutera jusqu'à ce qu'elle atteigne finalement l'instruction d'EBFE qui la coincera dans une boucle sans fin et EIP ne changera pas. La souche d'upx a maintenant unpacké l'APP en mémoire et au courant du saut vers l'OEP.

Ensuite nous préchargeons le champ du flag de notre structure de CONTEXTE vide avec CONTEXT_FULL pour permettre à GetThreadContext de lire toutes les valeurs de registre et comparons dans eax la valeur actuelle d'EIP (ThreadContext.regEip) avec l'adresse de notre point d'arrêt (c'est-à-dire si nous sommes restés fidèles au point d'arrêt encore). Si nous ne sommes pas encore au point d'arrêt nous simplement la pause pour 10 millisecondes et essayons GetThreadContext de nouveau.

Si nous sommes au point d'arrêt nous suspendons le Thread et lirons les bytes à notre adresse de patchée de la cible (qui a été maintenant décompréssée dans la mémoire) pour vérifier encore si c'est correct classons les versions. Si nous avons la version correcte que nous reconstituons le saut original vers l'OEP et écrirons en notre byte patché avant de reprendre le thread et sortir, laissant notre cible patchée s'executer correctement. Si nous avons une version incorrecte de la cible nous la rapportons et terminons le Thread suspendu avant de sortir.

Notez que le CONTEXTE struct la définition doit entrer à la data section (.data?) non initialisée autrement GetThreadContext retournera une erreur, tandis que le StartupInfo et ProcessInfo Structs ne sont pas affairés et peuvent entrer à .data ou .data ?


ARTeam Tutorial

Concevoir un fichier PE

Nouveaux Concepts:

Utilisation d'une boîte de dialogue commune pour ouvrir un Fichier

Travail avec Structures - la structure d'OpenFileName
- classement des en-têtes PE

Nouvelles fonctions API:

GetOpenFileName

Dans ce Tutorial, nous coderons une application simple qui utilise les fenêtres dialogbox communes pour ouvrir un fichier exécutable et nous dire si c'est vraiment un fichier de PE valable. Le code est basé sur le Tutorial PE 2 par Iczelion, mais avec quelques changements et la structure de SEH décompté pour simplifier un peu les choses à notre but. L'idée est ici de s'habituer au traitement de structures tant en fondant la structure d'OPENFILENAME qu'en naviguant dans les en-têtes PE.

Pour démontrer qu'un fichier est un fichier de PE valable, d'habitude il est seulement nécessaire de montrer que le fichier commence par le texte "MZ" au début de l'en-tête de DOS et que la fin de l'en-tête de DOS contient un indicateur sur l'en-tête PE qui commence par le texte "PE" suivi par 2 NULS. Ces signatures ont été commodément définies dans windows.inc:

IMAGE_DOS_SIGNATURE equ 5A4Dh ("MZ")
IMAGE_NT_SIGNATURE equ 00004550h ("PE" 00 00)

Ouvrir WinAsm puis ouvrir un nouveau projet, ensuite ajoutez une nouvelle classe .rc et disposez une boite de dialogue comme cela :

Le script validate.rc est inclus à un endroit habituel. Le bouton étiqueté de 3 points permet de trouver un fichier dans son dossier de destination et pour l'ouvrir au lieu de taper le chemin complet dans l'editbox. Collez ensuite le code validate.asm dans WinAsm :

Pour la plupart de cela, ça devrait être évident maintenant. Il y a 3 nouvelles variables dont nous devons en discuter. On déclare la structure d'OFN(OpenFileName) dans windows.inc qui est comme suit :

Comme expliqué dans le dernier chapitre, c'est juste un modèle. Pour l'utiliser dans notre code nous devons en définir un model et lui donner un nom comme une variable. Nous l'appellerons ofn, mais ne pas initialiser encore n'importe laquelle de ses valeurs, donc nous utiliserons la ligne suivante:

.data?
ofn OPENFILENAME <>

Seulement 5 champs sont importants pour nous et doivent être remplis avant l'utilisation de l'API GetOpenFileName. Ceci est fait dans la Procédure de Dialogue après le "..." et que le bouton soit cliqué (à examiner dans le fichier).

L'opérateur SIZEOF retourne la taille de l'ofn et structure la variable que nous avons définie susdit. Le champ lpstrFilter indique à la variable strFilter contenant le texte qui apparaît dans le fichier en tapant dans le menu déroulant du dialogue ouvert. Le champ lpstrFile indique au buffer TargetName où le nom et le chemin du fichier ouvert seront stockés. Le champ nMaxFile contient le nombre maximal des caractères du nom de fichier et du chemin (la taille Maximum du buffer).

GetOpenFileName remplit alors le buffer TargetName avec le nom et le chemin du fichier choisi dans le dialogue ouvert. Si c'est un succès, on montre le nom dans la boîte d'édition et le buffer est vidé pour la prochaine fois avec la fonction RtlZeroMemory.

En cliquant sur le Bouton GO! , le nom du fichier est recouvert dans l'editbox, vérifié pour assurer qu'il y a en réalité eu une certaine saisie de l'utilisateur et si oui, passez à la procédure de Validation.

Le fichier cible est ouvert, un objet de configuration créé et une vue du fichier entier a dressé le plan en mémoire. Toutes ces opérations sont effectuées avec l'accès en lecture seule, indiqué ici comme c'est tout ce qui est nécessaire. Comme décrit auparavant, MapViewOfFile retourne un indicateur(pointer) dans eax au premier octet du fichier dans la mémoire.

La partie suivante illustre une façon légèrement différente d'utiliser des structures pour naviguer dans les en-têtes PE. Les structures d'en-tête sont déjà présentes dans windows.inc et cette fois-ci, nous ne devons pas définir n'importe quelles variables de structure. Pour cela nous les utiliserons comme nous les utiliserions, seulement comme des modèles pour super imposer à notre vue de fichier dans la mémoire.

D'abord nous avons besoin de l'en-tête de DOS et les seuls champs par lesquels nous nous sommes intéressés, sont les premiers et derniers membres a appelés e_magic et e_lfanew :

D'abord nous devons regarder la valeur dans e_magic. Pour l'expression, c'est bien sûr IMAGE_DOS_HEADER.e_magic pour ainsi le super imposer sur notre vue de fichier et ensuite que nous prenons l'indicateur dans eax et faisons [eax. IMAGE_DOS_HEADER.e_magic]

Si la valeur dans e_magic est égale à ce que défini IMAGE_DOS_SIGNATURE, nous devrions continuer à récupérer la valeur dans e_lfanew et l'ajouter à notre indicateur pMapping pour que cela l'indique maintenant à l'en-tête PE :

Maintenant nous sommes à l'en-tête PE et nous regardons la première "Signature" des champs pour voir si la valeur est égale à ce qui est défini dans IMAGE_NT_SIGNATURE et si oui, donc nous disons que le fichier est valable.

Le code montré utilise ici des noms de structure pour garder des choses lisibles mais vous trouverez souvent du code source où des champs divers qui ont accès en ajoutant des offsets connues dans le formulaire numérique. Par exemple, le champ e_lfanew est toujours à 3Ch des octets du début du fichier et c'est pour obtenir l'en-tête PE que vous verrez souvent ajouter eax, [eax+3Ch] au lieu d'ajouter eax,[eax.IMAGE_DOS_HEADER.e_lfanew]. Il y a d'autres exemples où cela arrive. Dans le tutorial suivant, nous nous fonderons sur ce code avec une utilité pour ajouter une section à un PE de fichier.


ARTeam Tutorial

Code Squelettique d'une DLL

Nouveaux Concepts:

Bibliothèques et Jonctions
La procédure de la DLL EntryPoint
Le code squelettique pour créer des DLL
L'utilisation d'un fichier DEF pour exporter des fonctions

Les DLL ont quelques utilisations importantes dans le Reverse. Tout d'abord il est utile d'être capable d'écrire des plugins pour vos outils de reverse favoris comme ImpREC, Olly et PEID ou même votre IDE favori. Les Plugins prennent la forme d'une DLL utilisée par l'application.

L'utilisation la plus importante est l'injection d'une DLL dans laquelle une DLL peut être forcée à être chargée dans l'adresse espace d'un processus cible. Comme nous l'avons déjà vu avant, elle peut avoir accès à toute la mémoire du processus et contrôler l'application entière. Un bon exemple pour une utilisation, ce sont les articles sur l'unpacking avec des traceurs par NCR. Ceci est une structure avancée et dans ce Tutorial nous commencerons à apprendre et à comprendre comment fonctionnent les DLL et regarderons le code squelettique(Les bases du code) pour écrire cette DLL.

Les petits bouts de code ou de fonctions Fréquemment utilisés sont souvent stockés sous forme compilée à l'extérieur de l'exécutable qui les utilises dans des modules appelés des bibliothèques. Il y a 2 principaux types :

Bibliothèques Statiques

Ceux-ci sont utilisés principalement pendant le développement du programme. Dans un processus qui appelle la jonction statique, le linker(liaison) copie les fonctions exigées de la bibliothèque (qui d'habitude a l'extension .LIB) directement dans d'autres modules pour former le fichier exécutable final. Les bibliothèques, pendant l'exécution sont un bon exemple. L'inconvénient c'est qu'avec les exécutables, c'est l'utilisation des mêmes fonctions qui contiendra le code fait en double et consommera donc plus de RAM.

Bibliothèques de Liaison Dynamiques

Ceux-ci sont utilisés pendant l'exécution du programme. Dans ce cas, les fonctions de bibliothèque ne sont construites à aucun moment dans le fichier .EXE. Au lieu de cela ils résident dans un module séparé en formulaire exécutable (fichier au format PE) appelant une Bibliothèque de Liaison Dynamique. Cela contient des fonctions et des données qui peuvent être utilisées par un ou plusieurs autres modules ou "clients". Quand le premier client exige une DLL, Windows charge la DLL dans le client à l'adresse espace virtuel pour que le client puisse avoir accès aux fonctions dont il a besoin.

Les processus multiples qui chargent la même DLL à la même adresse de base partagent une seule copie de la DLL dans la mémoire physique. Le fait de faire cela, économise la mémoire du système et réduit l'échange. Tous les processus qui utilisent la même DLL partagent son code, mais ont leur copie unique propre de leur section de données.

Windows fournit deux méthodes pour dynamiquement lier une DLL; jonction implicite (une fois chargée) et jonction explicite (la durée d'exploitation). La différence se trouve avec le client; la DLL est la même dans les deux cas.

Jonction implicite(une fois chargée)

Windows charge la DLL automatiquement avec le premier programme du client et écrit les adresses de fonctions exigées dans la table d'adresse d'importation du client avant que le client ne commence l'exécution. C'est simple et n'exige aucun code supplémentaire, mais pour l'utiliser, l'application du client doit être lié avec une bibliothèque d'importation (le fichier .LIB) pour la DLL en utilisant la déclaration includelib.

Quand le module du client est construit, le linker(liaison ou lien) exécute n'importe quelle adresse pour arranger le fichier exécutable final, mais dans le cas des fonctions qui résident dans une DLL externe, le linker ne sait pas que ces adresses construisent un temps. Dans ce cas le linker utilise une bibliothèque d'importation pour la DLL (a appelé vaguement aussi le fichier .LIB). Une bibliothèque d'importation ne contient aucun code exécutable, elle nomme seulement les emplacements de fonctions exportées par la DLL. Le linker utilise les emplacements dans la bibliothèque d'importation pour résoudre des références aux fonctions de DLL externes du client.

En général, le chargement implicite est le meilleur cas quand un client exige toujours au moins une procédure dans la DLL, puisque Windows charge automatiquement la DLL du client. L'inconvénient de cette méthode est que si la DLL exigée est introuvable, Windows refusera d'exécuter votre application! Si vous chargez la DLL explicitement comme dans la méthode suivante et donc que la DLL non essentiel ne peut pas être trouvé, votre programme peut simplement informer l'utilisateur et continuer l'exécution.

Jonction explicite(la Durée d'exploitation)

Windows ne charge pas la DLL avant que le premier client ne le demande explicitement pendant l'exécution. Pour le faire, le client utilise les fonctions d'API suivantes :

LoadLibrary - Charge la DLL dans la mémoire
GetProcAddress - Obtenir l'adresse de chaque fonction exigée
FreeLibrary - Décharge la DLL quand les fonctions ne sont plus exigées

Bien que cela exige plus d'effort de programmation dans le client pour qu'il permette au contrôle que les DLL (si chacune) se chargent au temps exécuté. Un fichier de LIB pour la DLL n'est pas nécessaire bien que vous deviez toujours savoir combien de paramètres les fonctions prennent à l'intérieur de cela, etc. De plus vous pouvez appeler les fonctions non documentées qui ne sont pas détaillées dans les bibliothèques d'importation et sont souvent exportées par ordinal seulement.

Pour charger une DLL, implicitement ou explicitement, Windows cherche le fichier DLL dans les répertoires suivants dans l'ordre montré:

1. La liste d'adresses actuelle
2. La liste d'adresses de Windows, qui contient par exemple WIN.COM
3. La liste d'adresses du système de Windows, qui contient des fichiers du système comme GDI.EXE
4. La liste d'adresses où le programme du client réside
5. Répertoires inscrits dans le string d'environnement du CHEMIN
6. Répertoires qui dressent le plan d'un réseau

Si Windows ne localise pas la DLL dans n'importe lequel de ces répertoires, il avertit l'utilisateur avec une boîte de message.

Code de la DLL

Le code dans une DLL consiste à des fonctions exportées et non exportées. Les fonctions Exportées sont des routines publiques servant des clients tandis que les fonctions non exportées fournissent l'appui privé, interne pour les procédures exportées et ne sont pas visibles à un client. Les fonctions Exportées doivent être inscrites dans la section EXPORTATIONS d'une définition de module (le fichier .DEF) - un fichier texte contenant des informations utilisées par le linker. Nous en discuterons bientôt.

La Fonction DLLEntryPoint

Une DLL a d'habitude une fonction de point d'entrée comme indiqué ci-dessous (cela ne doit pas être appelé DLLENTRYPOINT, il peut être appelé autrement, comme eg. LibMain qui est généralement utilisé.) le système appelle la fonction de point d'entrée chaque fois qu'un processus ou un thread chargent ou déchargent la DLL. Il peut être utilisé pour exécuter l'initialisation simple ou des tâches de nettoyage.

Le système appelle la fonction de point d'entrée dans le contexte du processus ou du thread qui a fait que la fonction a été appelée. Cela permet à une DLL d'utiliser sa fonction de point d'entrée pour allouer la mémoire dans l'espace d'adresse virtuel du processus d'appel ou ouvrir des handles accessibles au processus. La fonction de point d'entrée peut aussi allouer la mémoire qui est privée à un nouveau thread en utilisant le thread du stockage local (TLS).

DLLENTRYPOINT typique y ressemble (voir Win32.hlp):

Autrement dit, quand Windows appelle votre DLLENTRYPOINT, il passe 3 paramètres mais le 3ème n'est pas utilisé :

hInstDLL est le handle du module de DLL. Ce n'est pas le même comme le cas de handle du processus et est utile si par exemple votre DLL a des ressources comme ceux-ci qui ont accès par le handle de DLL. Vous devriez garder cette valeur si vous deviez l'utiliser plus tard comme vous ne pouvez pas l'obtenir de nouveau facilement. La valeur ne change pas à moins que la DLL ne soit déchargée et chargée de nouveau donc elle est sûr d'être stocker dans une variable global.

fdwReason peut être une des quatre valeurs :

DLL_PROCESS_ATTACH - la DLL reçoit cette valeur quand elle est d'abord injectée dans le processus de l'adresse espace. Vous pouvez utiliser cette occasion de faire l'initialisation.

DLL_PROCESS_DETACH - la DLL reçoit cette valeur quand elle est déchargée du processus de l'adresse espace. Vous pouvez utiliser cette occasion de faire un certain nettoyage en désaffectant de la mémoire.

DLL_THREAD_ATTACH - la DLL reçoit cette valeur quand le processus crée un nouveau thread.

DLL_THREAD_DETACH - la DLL reçoit cette valeur quand un thread dans le processus est détruit.

Vous devez retourner VRAI dans eax si vous voulez que la DLL continue à fonctionner. Si vous retournez FAUX, la DLL ne sera pas chargée. Par exemple, si votre code d'initialisation doit allouer un peu de mémoire et qu'il ne peut pas le faire avec succès, la fonction d'entrypoint devrait retourner FAUX pour indiquer que la DLL ne peut pas fonctionner.

Pour des processus utilisant la jonction implicite (une fois chargée), une valeur de retour de causes FAUSSES à l'initialisation du processus le fait échouer et le processus se termine comme nous avons dit susdit. Pour des processus utilisant la jonction explicite (la durée d'exploitation), une valeur de retour de causes FAUSSES et c'est le LoadLibrary ou la fonction de LoadLibraryEx qui les font rendre un NUL, alors l'échec de l'indication et le système déchargent simplement la DLL.

D'autres Fonctions

Vous pouvez mettre vos fonctions n'importe où dans la DLL, mais si vous voulez qu'elles soient accessibles à d'autres modules, vous devez mettre leurs noms dans la liste d'exportation dans un fichier DEF comme nous avons dit susdit. Ensuite nous regarderons le code essentiel pour une DLL et son fichier DEF.

Le fichier ASM

Créez un nouveau projet dans WinAsm vide, mais choisissez cette fois le "Standard DLL":

Sauvez maintenant le projet et coller le code de DLLSKELETON.ASM dans WinAsm:

Si le DLLENTRYPOINT proc est seulement pour être appelé une fois quand la DLL est chargée, alors les conditions traitant les autres situations peuvent être omises et "vrai" peut être rendu immédiatement. Cela semble beaucoup plus simple - voir le Tutorial suivant par exemple.

Note: L'avancement soulignée pour nos noms de fonction exportés n'est pas nécessaire mais beaucoup de programmeurs l'ajoutent pour distinguer leurs fonctions propres de l'API.

Le fichier DEF

Créez ensuite un fichier texte vide dans le dossier de projet et rebaptisez-le DLLSKELETON.DEF puis dans WinAsm, cliquez sur le bouton pour Ajouter des Fichiers (voir la photo en dessous):

Double-cliquez sur le fichier de DLLskeleton.def et il devrait apparaître dans la barre d'explorateur de projet :

Maintenant WinAsm changera les options linker correctement pour l'utiliser et vous serez capables de l'éditer directement dans la fenêtre de code. Ajoutez les lignes suivantes :

LIBRARY DLLskeleton
EXPORTS _ExpFunc1 @ 01
_ExpFunc2 @ 02
_ExpFunc3 @ 03

Les mots de la BIBLIOTHÈQUE et des EXPORTATIONS sont des déclarations de définition de module. Il y a beaucoup d'entre eux détaillés dans la documentation masm61 mais ceux-ci sont les 2 seuls que nous devons connaître:

La déclaration de bibliothèque n'est pas en réalité exigée - notre projet sera toujours construit comme une DLL sans cela.

Notez s'il vous plaît le mot-clé NONAME ci-dessus pour exporter des fonctions par ordinal seulement. Nous l'utiliserons dans le Tutoriel suivant. Pour plus de renseignements sur des exportations et ordinals voir mon Tutorial PE. Notez que dans les commentaires, on inclus des fichiers qui ont la même syntaxe dans des fichiers de def que dans des fichiers asm.

Le fait de cliquer sur Go, tous les fichiers dont on a besoin vont se créer, non seulement la DLL, mais aussi un fichier LIB que vous pouvez utiliser pour avoir accès à vos fonctions par la jonction implicite (une fois chargée):

Dans le Tutorial suivant nous regarderons les différentes façons d'appeler des fonctions conservées dans une DLL.


ARTeam Tutorial

Exemple d'une DLL Simple

Nouveaux Concepts:

L'étude pour appeler des fonctions exportées par une DLL (Dynamic Link Library)

fichier contenant une bibliothèque de fonction utilisée par les programmeurs.

Nouvelles fonctions API:

LoadLibrary
GetProcAddress
FreeLibrary

Dans ce chapitre nous coderons une DLL simple avec des fonctions exportées et regarderons les façons différentes d'appeler la fonction de notre application client. Dans le premier exemple nous utiliserons la jonction implicite(une fois chargé) et dans le deuxième nous utiliserons la jonction explicite(la durée d'exploitation). Dans le deuxième cas nous importerons une des fonctions par ordinal. C'est la méthode que nous devons utiliser pour avoir accès aux API non documentées comme les noms de fonction n'apparaîtront pas dans la DLL ou de la bibliothèque d'importation disponible.

D'abord nous construirons une DLL pour exporter 2 fonctions simples - un de nom et l'autre par ordinal seulement. Créez un nouveau projet dans WinAsm et s'assurer que vous utilisez les fixations DLL dont on a déjà discutées dans le dernier Tutorial. Maintenant collez le code de MyDLL.asm dans WinAsm:

Ajoutez un fichier DEF avec les lignes suivantes:

EXPORTS

_ExpFunc1 @ 01
_ExpFunc2 @ 02 NONAME

Souvenez-vous dans le dernier Tutorial que le mot-clé NONAME arrête le nom de fonction étant placé dans la DLL et l'exporter par ordinal seulement. Cliquez Go pour créer les Fichiers MyDLL.LIB et MyDLL.DLL. Si vous le souhaitez, vous pouvez vérifier la DLL résultant avec n'importe quel PE Explorateur d'outils, par exemple CFF Explorer par Ntoskrnl:

Notez que la fonction avec la valeur ordinale 2 n'a aucune valeur dans les champs de nom et aussi que NumberOfFunctions est 1 fois plus grand que NumberOfNames.

Pour utiliser le fichier LIB pour lier une application avec notre nouvelle DLL au temps de charge, nous avons aussi besoin d'un fichier INC. Cela peut être fait avec l'utilitaire lib2inc, mais peut aussi être écrit à la main comme un fichier texte et renommer MyDLL.INC. Il contient simplement une liste des prototypes de toutes les fonctions exportées par la DLL et de la même manière nous écrivons des prototypes pour nos procédures propres. Pour utiliser lib2inc ouvrant une commande sans faute du dossier contenant le fichier lib, lib2inc et le type "lib2inc MyDLL.lib", voir l'image ci-dessous (sans citations) :

Cela créera un fichier INC simple comme décrit ci-dessus:

Maintenant nous coderons une application rapide pour lier notre DLL une fois chargée et importerons les fonctions. Lancez WinAsm et faites un nouveau projet - Standard exécutable cette fois-ci. Collez le code Implicit.asm dans WinAsm:

Cet exemple d'application appelle simplement les 2 fonctions l'une après l'autre et quitte ensuite. Notez bien que la deuxième fonction est exportée par ordinal seulement et que le nom n'est pas incorporé dans la DLL, donc nous pouvons toujours l'appeler de nom(ex: include MyDLL.inc, include MyDLL.lib, etc...) parce que nous avons les fichiers LIB et INC.

Maintenant nous changerons la susdit application pour qu'elle utilise la jonction de durée d'exploitation. La première fonction sera appelée de nom et la deuxième par ordinal. Créez un nouveau projet et puis coller le code de Explicit.asm dans WinAsm:

Dans ce cas nous utilisons LoadLibrary pour charger notre DLL dans le processus adresse de l'espace en rendant son handle, puis GetProcAddress pour trouver l'adresse des fonctions que nous voulons appeler. GetProc peut utiliser un indicateur sur le nom de la fonction ou la valeur ordinale de la fonction.

Notez que nous n'avons pas besoin d'un lib ou d'un fichier inc, mais si la fonction appelée avait eu besoin des paramètres spécifiques, nous devrions connaître ceux pour l'utiliser correctement.

Finalement FreeLibrary est utilisé pour décharger la DLL. Cela arriverait en réalité automatiquement après l'appel à ExitProcess mais c'est la bonne pratique de codage pour le faire de toute façon.


ARTeam Tutorial

Coder un Plugin pour ImpREC

Comme nous l'avons dit auparavant, plugins pour tous nos outils favoris de reverse ont tendance à se présenter sous la forme de DLLS. Dans cet exemple nous regarderons le codage pour ImpREC comme c'est assez simple. ImpREC est un outil excellent pour réparer ou reconstruire les importations d'exécutables corrompu par des packeurs. Il échoue sur certains des protecteurs plus récents, mais il restes plein de ressources comme c'est extensible via plugins. Les tutoriaux quant à son utilisation sont abondants sur le web et il est détaillé aussi dans mon tutorial sur les fichiers PE.

MackT a fourni des instructions à l'auteur d'ImpREC dans Plugin.txt dans le dossier où ImpREC est installé :

Les points importants ci-dessus sont encadrés en rouge. La chose suivante est le code source pour tELock v0.92 . Je ne l'ai pas codé moi-même et je ne sais pas qui est l'auteur . C'est un bon exemple comme c'est très simple. J'ai ajouté plus de commentaires pour expliquer chaque ligne de code. Si vous voulez faire un projet WinAsm et construire cela rappelle la description des DLL décrites dans le chapitre squelette DLL. Asm et des fichiers de Def sont dans la section de code source comme d'habitude.

Notez qu'il y doit toujours avoir une procédure DLLENTRYPOINT vide (appelé LibMain dans ce cas) et que le seul l'autre procédure est notre fonction de trace construite selon les instructions de MackT.

Le fichier DEF est aussi très simple comme d'habitude contenant seulement 2 lignes :

LIBRARY tELock
EXPORTS Trace

Coder un plugin pour OllyDbg

C'est exactement le même principe mais OllyDbg un logiciel un peu plus complexe . A PDK (plugin development kit) est disponible en téléchargement dessous:

http://www.ollydbg.de/plug110.zip

Qui inclut la documentation et 2 échantillons entièrement fonctionnels et bien faits avec des remarques. Dans la documentation :

Un Plugin est une DLL qui réside dans le répertoire d'installation d'OllyDbg et ajoute une fonctionnalité à celui-ci. Pour information,certain plugin différent exige des noms uniques.udd (udd) comme extension, nametypes etc... Pendant le démarrage, OllyDbg charge toutes les DLLS disponible une à une et cherche des points d'entrée nommés _ODBG_Plugindata et _ODBG_Plugininit. Si ces plugins sont pris en compte et compatibles ou déja présent dans le répertoire "plugin", OllyDbg enregistre ces plugins et les ajoute dans les sous-menu au menu contextuel Plugins dans le menu principal d'OllyDbg.

Les Plugins peuvent êtres ajoutés dans le menu comme Disassembler, Dump, Stack, Registers, Memory, Modules, Threads, Breakpoints, Watches, References, Windows and Run trace windows etc... Ils peuvent être considérés raccourcis globaux,comme des raccourcis d'une des fenêtres inscrites. Ils peuvent aussi créer des fenêtres MDI propres. Les Plugins peuvent écrire des données plugin-spécifiques aux fichiers de .udd avec des informations dépendantes de module et ollydbg.ini et avoir accès aux structures de données différentes qui décrivent l'application débuguée. Il y en a plusieurs en général, des fonctions de rappel de service qui permettent l'interaction facile mais proche avec OllyDbg.

Malheureusement, les deux échantillons de plugins sont écrit en C avec des instructions pour être compilée sous BCC55, BCB5 ou M$VC ++.J'ai pu avoir un exemple autour duquel j'ai vu qu'il a été codé en MASM par SV et EviLoid. Il a fait là un exemple idéal parce que c'est une réparation simple pour l'API IsDebuggerPresent qui est aussi importante pour notre compréhension. Ce n'est pas compilable cependant comme quelques fichiers d'inc manquent mais j'ai inclus l'asm et les fichiers de def dans la source ICI.


ARTeam Tutorial

Conclusion

J'espère que cela servira à beaucoup d'entre vous, et surtout qu'il motivera beaucoup de personnes à commencer à coder. Ce n'est pas vraiment si difficile que ça ,une fois qu'on s'est jeté à l'eau et cela commence à vous sembler plus clair et résout bien des problèmes. Je vous fais mes excuses si mon code vous semble un peu confus (Je n'est pas cherché à l'optimisé pour le rendre le plus lisible), mais je me considère toujours comme un néophyte. Le travail des amis et la diffusion libre que vous apprenez. si vous avez des commentaires, questions ou des corrections entrez s'il vous plaît en contacte avec moi goppit@hotmail.com

Références & Bibliographie

Le matériel de lecture le plus essentiel inclut les tutoriaux par Iczelion sur le codage de masm et le format de PE - ceux-ci ont été le point de départ pour la plupart des codeurs asm d'hier et d'aujourd'hui. Ce souci asm codant en général plutôt qu'être RCE-BASE et la plupart des exemples utilisent des applications à fenêtres complètes plutôt que dialogbox de base, que j'ai évité ici de vous mettre ici pour gagner du temps et j'ai gardé les choses simples. Les sections sur le format de PE et l'API de Mise au point en font partie.

http://web.sau.edu/LillisKevinM/csci240/masmdocs/

http://win32assembly.online.fr/

La meilleure source d'informations sur la syntaxe de MASM est le Guide de MASM 6.1 Programmeur et MASM 6.1 Référence par M$ et 1992 daté. Une collection de cette documentation est disponible ici :

http://web.sau.edu/LillisKevinM/csci240/masmdocs/

De plus,qu'Intel product à fait un tas de manuels énormes d'en environ 5 volumes détaillants le jeu d'instruction complet pour les processeurs x86. Ceux-ci sont très détaillés et probablement de peuvent servir aux débutants, et sont disponibles en téléchargement ici :

http://www.intel.com/design/pentium4/manuals/index_new.htm#1

Il y a aussi des liens sur la même page où trouver les manuels gratuitement sur CDROM ou sur papier!

Le forum Win32AsmCommunity et MASMFORUM sont la source suivante la plus de valeur d'informations :

http://win.asmcommunity.net/board/index.php

http://www.masmforum.com/simple/index.php

Le meilleur ebooks que j'ai vu sur l'assembleur est comme suit ::

Assembleur Point par point : la Programmation avec le DOS et le 2ème Rédacteur Linux - Jeff Duntemann (John Wiley et des Fils 2000)
l'Art d'Assembleur - Randall Hyde 2003)
l'Introduction à 80x86 l'Assembleur et l'Architecture Informatique - Richard C. Detmer (Jones et Bartlett Publishers 2001)
la Programmation de Fenêtres dans l'Assembleur - Randall Hyde
des Assembleurs et des Chargeurs - David Salomon (Ellis Horwood)
Linkers et Loaders - John R Levine

Le meilleur ebooks que j'ai vu sur l'architecture des APIs de Windows est :

Programming Windows 5th Ed - Charles Petzold (M$ Press 1998)
Programming Applications for M$ Windows 4th Ed - Jeffrey Richter (M$ 1999)
Windows Internals 4th Ed - Mark Russinovich & David Solomon (M$ Press 2004)
Reversing: Secrets of Reverse Engineering - Eldad Eilam (Wiley Publishing 2005)

Ceux-ci sont tous disponibles en pdf ou en format chm à de divers endroits sur le net.

Finalement je mentionnerai de nouveau la série excellente de tutoriaux d'ARTEAM sur le cracking ,sur les loaders et les articles de traceurs par NCR. Il y a aussi beaucoup de code source intéressant partagé tous deux dans les tutoriaux mentionnés, mais également sur les forums de reverse.

Salutations et Remerciements

Salutations et Remerciements à tous les membres d'ARTEAM et aux lecteurs. Grâce à tous, la communauté du reverse partage librement les codes et les informations.

je salue et remercie tout particulièrement Shub, Nilrem, Gabri3l, lena151 et bien sûre Iczelion... et tous ceux que je n'ai pas cités.

Traduction Française

Mars 2008

Nous tenons à nous excuser par avance si toutefois lors de la lecture vous avez rencontré des phrases difficiles à comprendre. Cela vient du fait que nous ne sommes pas traducteurs de métier, nous l'avons traduit pour rendre service à certains qui ont déjà beaucoup de mal à comprendre certains termes techniques du reverse. Soyez donc indulgent, nous avons fait de notre mieux. Merci.

Sincères Salutations et Remerciements à Goppit pour cet excellent Tutorial.

Merci à Goldocrack de la Team ODC de nous avoir conseillé ce tutorial

sans qui nous n'aurions jamais eu ni l'idée ni l'envie de le traduire.

@++

SPOKE3FFF & DcSpirou

Goldocrack de la Team ODC

http://xtx.free.fr/


ARTeam Tutorial

Installation de MASM32 et WinAsm Studio

Installation de MASM32

Téléchargez tout d'abord MASM32 et le pack WinAsm (les liens sont à la page d'introduction). Dézipez m32v82r.zip et exécutez le INSTALL.EXE qui vous demandera alors de spécifier le chemin d'installation. Si vous choisissez C:\ par conséquent l'adresse d'installation sera C:\masm32\. Tous les utilitaires et les programmes de même types qui viennent avec Masm32 s'attendront à ce que ce soit installé directement de la racine de la partition.

Le programme d'installation ouvrira une fenêtre de type console(MSDos) demandant pour construire les bibliothèques MASM , patientez car cela peut prendre quelques minutes :

Après à la fin de processus de construction pressez n'importe quelle touche du clavier , vous devriez obtenir une fenêtre avec le message "installation success".

Le conseil de Hutch qui a réuni le pack MASM32, est que les bibliothèques devraient toujours être construites sur votre propre system plutôt que des copies venant d'ailleurs , cela est conseillé afin d'empêcher la corruption de fichiers essentiels.

Installation de WinAsm Studio

1. Installez-le en décompressant le zip dans C:\Program Files\WinAsm Studio

2. Lancez WinAsm et allez dans Tools>Options>Files&Paths et vérifiez que tous les paths (Fichiers d'aide doivent être redirigés vers WIN32.HLP et le Chemin Projets vers le répertoire de votre code source)::

3. Un débogueur et un éditeur de ressource peuvent être configurés Tools>Options>Miscellaneous:

Quand vous aurez un projet ouvert dans WinAsm en appuyant Ctrl+F9 cela ouvrira Olly avec le projet actuel exécutable chargé, il faut que la checkbox de "Use quotes for file name"et aussi l'autre checkbox soient cochées comme sur l'image au dessus ,pour que cela fonctionne parfaitement.

4.Prenez comme référence, mes paramètres généraux:

5. Un fichier d'aide (WinAsm.chm) est disponible sur le site Web qui devra se trouver dans le répertoire C:\Program Files\WinAsm Studio\Help.

6. Le Tools Manager (sous le menu d'Outils) vous permet d'ajouter des entrées pour vos utilitaires favoris dans le menu outils. Il y a des outils très utiles à l'adresses d'installation masm32 qui valent la peine d'être ajoutés - particulièrement MNEMONIX.EXE (qui convertira n'importe quelle donnée hexadécimale opcode en son plus significatif mnémonique) et LIBLIST.EXE (qui vous dira quelle DLL et n'importe quelle fonction d'API donnée ou elle réside). J'ai aussi ajouté des entrées pour Table Extractor, RGN Creator et plusieurs fichiers d'aide.

7. Des Add-Ins Manager peuvent être ajoutés et sont disponible sur le site Web WinAsm. En particulier le Nouvel assistant de Projet est une grande amélioration sur la fonctionnalité existante. Pour utiliser un Add-in - il faut simplement copier la DLL dans le répertoire C:\Program Files\WinAsm Studio\AddIns , ouvrez Add-Ins Manager (Add-Ins menu) et configurez-le comme vous le souhaitez. Je vous recommande aussi de regarder le Quick Help System et l'Aide de H2 Context Help add-ins.

QHS ouvre un encadré contenant une définition du mot sous le curseur dans la fenêtre de l'éditeur de code. Les définitions de chaque mot sont stockées comme des fichiers de .rtf séparés dans le répertoire C:\Program Files\WinAsm Studio\Help\QHS\ donc vous pouvez facilement ajouter à ce qui est déjà là en écrivant vos propres définitions de mots.

H2 Context Help add-in est excellent si vous installez la Plate-forme SDK. Il requière le logiciel freeware H2 programmé par Rob Chandler et disponible ici : http//helpware.net/mshelp2/h2viewer.htm et montera le fichier d'aide de MSDN pour le mot sous le curseur dans WinAsm en appuyant F1. Si vous avez le vieux fichier de Win32.hlp configuré comme ci-dessus il est toujours disponible sous le menu d'aide dans WinAsm.

Diagnostic des erreurs

Si la signalisation d'erreur de syntaxe WinAsm ne fonctionne pas, alors la cause la plus probable est que les chemins d'accès sont mal configurés (voir ci-dessus).

L'erreur suivante peut arriver avec masm si le code source est sur une partition différente du dossier d'installation masm :

fatal error A1000: cannot open file : \masm32\include\windows.inc

Cela peut être évité par précédent Include et includelib dans le code source par leur chemin complet par exemple "include C:\masm32\include\windows.inc" plutôt que juste "include windows.inc". Cependant si les chemins sont configurés correctement comme ci-dessus alors WinAsm devrait donner les chemins corrects à l'assembleur et linker automatiquement

Personnalisation de votre installation MASM32

Le pack MASM32 Contient la version 6.14 de ml.exe (l'assembleur) et la version 5.12 de link.exe (le linker) parmi d'autres fichiers binaires. Le pack peut être "mis à jour" en recopiant les fichiers dans C:\masm32\bin\ avec la version plus récente 7.10 fichiers Visual Studio 2003. Ceux qui sont inclus sont les suivants:

cvtres.exe
dumpbin.exe
editbin.exe
lib.exe
link.exe
ml.exe
nmake.exe
rc.exe
msdis140.dll
msobj71.dll
mspdb71.dll
rcdll.dll

Cette version est significativement plus rapide en assemblant et liant de grands projets et est probablement la meilleure version. Des versions plus récentes sont disponibles, mais sont malheureusement infectées de l'embonpoint de .NET.

Suivant votre installation vous pouvez avoir une erreur après la mise à jour des outils v7.10 quand le linker est utilisé - "This application has failed to start because MSVCR71.dll was not found". Cela fait partie de la Bibliothèque Pendant l'exécution M$ Visual C Runtime Library et si vous n'avez pas cette DLL,elle peut être téléchargée librement sur de nombreux sites Web - Avec Google vous pouvez trouver.

PE des fichiers produits utilisant M$ des outils de développement contiennent des octets supplémentaires "garbage code" à l'origine du DOS. Ceux-ci sont insérés par le linker Link.exe à compilent le temps. Dans tous les cas, le nec plus ultra reste DWORD. Ces données ne sont pas présent dans les fichiers produits avec d'autre linkers (eg Borland, GCC, fasm, golink, polink, etc). Ce comportement est exposé par toutes les versions de M$ Link.exe de v5.12.8078 qui fait partie du pack MASM32, jusqu'à v7.10.3077 qui expédie avec dernier C Visuel ++ des packs.

Les données incluent les codes encryptés qui identifient les composants ont eu l'habitude de compiler le fichier de PE. On dit que cela a mené à la poursuite d'un virus comme il permet à M$ de prouver que le virus a été compilé sur votre PC.

Le dword après "Rich" est une clef produite par le linker qui se répète plusieurs fois dans les garbage data. Quand nous compilons un programme le compilateur met la string "@comp.id" suivi par un compilateur DWORD-TAILLE ID le numéro dans notre fichier d'obj. Quand nous nous lions notre obj classe le linker extrait le numéro de comp.id et XORS cela avec la clef et l'écrit dans "garbage" comme 2ème DWORD auparavant "Rich".

Les "@comp.id" variables sont codées en dur :
ML.EXE Ver.6.14.8444 -> comp.id is 1220FC (Vous pouvez chercher: FC2012)
ML.EXE Ver.7.00.9466 -> comp.id is 4024FA (recherche: FA2440)
ML.EXE Ver.7.10.2179 -> comp.id is 0F0883 (recherche: 83080F)
ML.EXE Ver.7.10.3077-> comp.id is 0F0C05 (recherche: 050C0F)
C++ Optimizing Compiler Version 12.00.8804 for 80x86 ->comp.id is 0B2306

Le 1er DWORD avant "Rich" est la clée XORée avec la constante 536E6144h codée en dure. Si nous cherchons "@comp.id" dans notre fichier obj et que nous substituons le DWORD Après cela avec des zéros nous verrons que le second DWORD avant "Rich" est égal à la clé (DWORD après "Rich") Relisez si ce n'est pas clair du 1er coup en regardant en même temps l'image du dessous.

Voici en exemple un simple "hello world" le type de programme codé dans MASM32 et ouvert dans l'éditeur hexadécimal. Les octets supplémentaires sont encadrés en rouge :

Heureusement il est possible de patcher le linker pour arrêter ce comportement. Il y a un utilitaire appelé SignFinder.exe par Astérix (inclus Ici) Qui vous permet de trouver rapidement le code qui a besoin du patch dans n'importe quelle version de Link.exe. Utilisation v5.12.8078 de MASM32 comme un exemple :

Ouvrez donc Link.exe dans Olly et appuyez Ctrl+G. Entrez 0044510C (l'adresse de signfinder ci-dessus + ImageBase de Link.exe qui est 400000). Alors le point culminant l'instruction s'ajoutant comme indiqué, clic droit avec la souris et sélectionnez binary>fill with NOPs:

Cela devrait ressembler à l'image ci-dessous:

Finalement clic droit de nouveau et sélectionnez Copy to exécutable>All modifications. Et le clic "copy all et le clic droit dans la nouvelle fenêtre qui s'ouvre et sélectionnez save file. Les autres versions de link.exe ont le même ordre de code à des emplacements différents qui sont patchés de la même manière.

Si nous utilisons le linker patché pour recompiler le même exemple de programme nous voyons que les octets supplémentaires ont disparus :

Les seules autres différences entre les 2 fichiers sont bien sûr e_lfanew (la compensation de l'en-tête PE), TimeDateStamp et SizeOfHeaders (qui est efficacement l' offset de la première section). Regardez mon PE tutorial pour plus de renseignements.

Assemblage Avec une extension shell

Ce trick (tour) vous permettra d'assembler des fichiers de .asm du menu en cliquant-droit dans l'explorateur qui peut sauver le temps de petits emplois rapides;-) < / la fonte > < p> < la taille de fonte = "3" > D'abord copiez et collez ce script dans votre texteditor, sauvez-le sous le nom de assemble.bat à l'emplacement C:\masm32\bin\ :

-----------copié&collé----------------

@if not exist %1.rc goto NoResource

rc /foRes.res %1.rc
cvtres /machine:ix86 /out:Res.obj Res.res
del Res.res
@if errorlevel 1 pause

ml /c /coff /Fo%1.obj %1
@if errorlevel 1 pause
link /SUBSYSTEM:WINDOWS %1.obj res.obj
@if errorlevel 1 pause
del %1.obj
del res.obj

@goto Exit

:NoResource
ml /c /coff /Fo%1.obj %1
@if errorlevel 1 pause
link /SUBSYSTEM:WINDOWS %1.obj
@if errorlevel 1 pause
del %1.obj

:Exit

-----------copié&collé----------------

Maintenant placez le patch variable dans le rep MASM. Pour WinXP, clic-droit sur Mon Ordinateur et selectionnez propriétés. Cliquez sur le label Advanced tab et ensuite le bouton Environnement Variables. CHEMIN de point culminant dans le carré inférieur , cliquez edit. Et ajoutez "C:\masm32\bin;" à la fin du patch de la variable. On devrait lire quelque chose comme:

%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;C:\masm32\bin;

Dans l'explorateur de Windows, allez dans tools>Folder Options>File. Cliquez "New" et "enter" à ".asm" dans la boîte d'Extension de Fichier. Maintenant cliquez "Advanced" et le tapez "Assembly Module" dans la première editbox. Cliquez "Newl" et tapez "Assemble" dans la boîte d'Action. Et pour finir cliquez "Browse" et naviguez jusqu'à assemble.bat dans l'application..

Et finalement dans l'explorateur de Windows, allez dans Tools>Folder Options>File Maintenant si vous cliquez droit sur un fichier .asm dans l'explorateur de Windows, vous devriez avoir l'option pour compiler. Le fichier batch compilera aussi et se liera dans un script de ressource.


ARTeam Tutorial

FAQ de Syntaxes MASM

Sommaire:

1. ADDR et OFFSET
2. Les crochets
3. L'opérateur PTR
4. Structures
5. Les constructions à un haut niveau dans MASM
6. Les opérateurs relationnels
7. Préservation des Registres

ADDR et OFFSET

L'OFFSET renvoie à l'adresse d'une variable. Il est utilisé pour spécifier la localisation plutôt que le contenu de la variable:

.data
MyVar db 77h ; Variable taille d'octet appelée MyVar initialisée à 77h

.code
mov eax, MyVar ; copie 77h en eax
mov ebx, offset MyVar ; copie l'adresse mémoire où 77h est stocké dans ebx

L'Offset peut aussi passer l'adresse d'une variable à une fonction en invoquant une instruction. Cependant, ça fonctionnera seulement pour des variables globales déclarées dans la section des .data ou .data? . Cela échouera avec des variables locales que vous déclarez sur l'entrée de votre proc d'utilisation de la déclaration LOCALE. Ceux-ci n'ont aucune compensation comme ils sont créés sur la pile au moment de l'exécution. .

L'opérateur ADDR résout ce problème. Il est utilisé exclusivement avec invoke pour passer l'adresse d'une variable à une fonction. Pour des variables globales il résume à une simple instruction PUSH ,pareil que si l'OFFSET avait été utilisée :

push GlobalVar

Cependant pour des variables locales ADDR traduit :

lea eax, LocalVar ; Il charge l'adresse effective de LocalVar dans eax
push eax

(Il est important de se rappeler qu'en utilisant addr avec des variables locales, le registre eax est utilisé plutôt que le laisser libre pour d'autre utilisation dans la procédure.)

Lea eax, LocalVar équivaux généralement à mov eax,offset LocalVar mais c' est 1 cpu cycle plus lent donc mov ... l'offset est préférée dans certains cas alors que d'autres des variables locales.

Les crochets

Les crochets indiquent généralement la valeur (le contenu) d'une variable par opposition à son adresse. Cependant, la syntaxe MASM diffère légèrement à celle d'autres assembleurs à cet égard. Dans MASM tout ceux-ci produisent la même instruction:

mov eax,1
mov eax,[1]
mov eax, DWORD PTR 1
mov eax, DWORD PTR [1]

MyVariable & [MyVariable]Les deux signifient la valeur de MyVariable

Beaucoup de programmeurs utilisent par habitude des crochets avec des variables pour dénoter le contenu ainsi cela rend le code source un peu plus lisible et il le rend plus facile au code d'être supporté par d'autres assembleurs.

Comme il a été dit ci-dessus, l'offset MyVariable & addr MyVariable les deux signifient l'adresse de MyVariable.

Quand c'est utilisé avec les registres, cependant, les crochets font vraiment la différence et ont tendance à pointer l'adresse mémoire :

mov ebx,eax ; Copie la valeur de eax dans ebx.

mov ebx,[eax] ; Copie la valeur à l'adresse mémoire de eax dans ebx.

mov [ebx],eax ; Copie la valeur de eax en mémoire à l'adresse dans ebx.

L'opérateur PTR

L'opérateur PTR dit à l'assembleur la taille des données dans des situations où la taille ne peut pas être tirée automatiquement. Par exemple :

MOV [eax], 0

Ce n'est pas une instruction valable parce que l'opérande de mémoire [eax] n'indique pas de taille. Si vous voulez copier le zéro de valeur dans un OCTET de la mémoire à [eax], vous utilisez l'OCTET la notation PTR donc l'assembleur sait combien utilise la mémoire. Pour vous aider à éclaircir cette confusion voici quelques exemples de base :

mov bl, [eax]

Ici, le compilateur sait que 'bl' fait 8 (bits) et vous chargez le processeur de remplir 'bl' de la valeur ([...]) trouvé à l'adresse d'eax. Puisque le compilateur sait que la limite est de 8 (bits), impliqués par le registre 'bl' dans la destination, il n'y a aucun problème.

mov bl, byte ptr [eax]

C'EST EXACTEMENT LA MÊME CHOSE . C'est inutile, mais peut-être plus lisible. Ainsi pourquoi voudriez-vous utiliser l'octet ptr ou le mot ptr précédant un "at-the-address-of" ([...]) la déclaration ? On voit la réponse ici :

mov [ebx], 3

Voici un dilemme pour le compilateur. Il dit "le mouvement la valeur 3 à l'emplacement de mémoire à l'adresse d'ebx". Le compilateur aime penser dans des (bits) et doit savoir si 3 est un OCTET, UN WORD ou un DWORD etc. Il ne sait pas combien de(bits) de mémoire pour utiliser pour stocker la valeur donnée!! Pour le clarifier, BYTE PTR est utilisé pour stocker 3 à un octet de mémoire à l'adresse indiquée. De même le WORD PTR utiliserait 2 octets de mémoire pour stocker 3 en mémoire :
mov byte ptr [ebx], 3

Si vous pensez que vous devez utiliser l'opérateur PTR, vous demander si le CPU peut calculer combien de (bits) pour se déplacer, par une certaine indication DIRECTE (comme dans l'exemple 'bl' ci-dessus).

Structures

Déclarer un type de structure :

Exemple STRUCT
Champ1
Champ2
Champ3
Exemple ENDS

Définir un cas de structure de variable non initialisée :

.data?
MyStruct1 Exemple <>

Ou un cas initialisé de la variable :

.data
MyStruct2 Exemple <5,67,89>

Avoir accès aux champs directement :

mov eax,Mystruct2.field2 ; copie 67 dans eax

Avoir accès aux champs indirectement :

mov ebx,offset MyStruct2 ;Ebx contient le pointeur sur la structure de MyStruct2

Syntaxe1:

mov eax,[ebx].Exemple.Champ2

Syntaxe2:

mov eax,[ebx.Exemple.Champ2]

Syntaxe3:

mov eax,[Exemple ptr eax].Champ2

Syntaxe4 - Utilisation de la directive ASSUME :

assume eax:ptr Exemple
mov [eax].Champ2
assume eax:nothing ; Rappelez-vous toujours un-assume quand c'est fini .

Voir le chapitre sur Structures de Données pour avoir un exemple d'accès aux champs dans des structures empilées.

Les constructions à un haut niveau dans MASM

MASM tient compte de plusieurs types de C de haut niveau et construit pour rendre le code source plus lisible. L'assembleur traduit ceux-ci en asm des instructions avant l'assemblage dans le code d'objet. Le .if - .elseif - .else - .endif ont été utilisés largement dans nos Procédures de Dialogue jusqu'ici :

Opérateurs Relationnels

Vous pouvez exprimer les conditions du .IF.REPEAT et des directives .WHILE utilisant des opérateurs relationnels le même comme dans C. MASM traduit ceux-ci pour comparer, évaluer et des instructions de saut conditionnelles. Ils incluent :

Une condition sans opérateurs évalue pour le nonzéro comme il le fait en C :
.WHILE (x) Est identique à .WHILE (x != 0) - while x est nonzero
.WHILE (!x) Est identique à .WHILE (x == 0) - while x est zero

Vous pouvez aussi utiliser des noms de flag (le ZÉRO ?, CARRY ?, OVERFLOW ?, SIGN ? Et PARITY ?) comme les opérandes eg. in .WHILE (CARRY?), la valeur du flag CARRY détermine le résultat de la condition.

Préservation des Registres

Une fonction de Win32 que vous appelez préservera toujours les registres de segment
et d'EBX, EDI, ESI et des registres d'EBP. Au contraire, ECX et EDX considèrent
Les registres scratch sont toujours non définis et reviennent d'une fonction de Win32.
EAX n'est jamais préservé non plus et comme nous avons vu ce que l'on dit, cela la plupart du temps
Contient une valeur de retour. Autrement, c'est vide. Donc si vous avez des valeurs importantes dans EAX, ECX ou EDX, vous devriez les préserver (pour les sauver sur la pile) avant l'appel d'une fonction d'API.

De même si vous utilisez EBX, EDI, ESI ou EBP dans votre fonction de rappel de service, n'oubliez jamais de les rétablir avant le contrôle retour Windows. Cela ne signifie pas que vous ne pouvez pas utiliser ces quatre registres, vous le pouvez. Assurez-vous juste de les rétablir en arrière avant le contrôle passant en revenant à Windows.


ARTeam Tutorial

Capture d'écran d'un Patch

J'ai inclus ici une capture d'écran d'un patch Diablo2oo2 (Rechercher et Remplacer Patch Engine) chargé dans WinAsm seulement pour la référence rapide. Ce logiciel peut être téléchargé à partir de son site dans un fichier zip ici:

http://diablo2oo2.di.funpic.de/downloads/dup.search.and.replace.patchengine.sourcecode.rar


skeleton.asm

.386
.model flat, stdcall
option casemap:none

include kernel32.inc
includelib kernel32.lib

.data
.data?
.const

.code
start:
invoke ExitProcess,0
end start


helloworld

.386
.model flat, stdcall
option casemap:none

include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib

.data
MsgBoxCaption  db "Simple MessageBox Program",0
MsgBoxText     db "Hello World!",0

.code
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess,0
end start


dialogbox1.asm

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		user32.inc
include		kernel32.inc
includelib	user32.lib
includelib	kernel32.lib

DlgProc		proto		:DWORD,:DWORD,:DWORD,:DWORD

.data
Message		db		"Hello World!",0

.data?
hInstance	HINSTANCE	?

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke DialogBoxParam, hInstance, 1001, NULL, addr DlgProc, NULL
    invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg == WM_COMMAND
        mov	eax,wParam
        .if eax==1003
            invoke SetDlgItemText,hWnd,1002,ADDR Message
        .elseif eax==1004
            invoke	SendMessage, hWnd, WM_CLOSE, 0, 0
        .endif
    .elseif	uMsg == WM_CLOSE
        invoke	EndDialog, hWnd, 0
    .endif

    xor	eax,eax
    ret
DlgProc endp

end start


dialogbox1.rc

#define IDD_DLG1001 1001
#define IDC_EDIT1002 1002
#define IDC_BUTTON1003 1003
#define IDC_BUTTON1004 1004

IDD_DLG1001 DIALOGEX 0,0,221,127
CAPTION "Simple Dialog Box Program"
FONT 8,"MS Sans Serif"
STYLE 0x10cc0000
EXSTYLE 0x00000000
BEGIN
    CONTROL "",IDC_EDIT1002,"Edit",0x50010080,29,21,158,20,0x00000200
    CONTROL "Say Hello",IDC_BUTTON1003,"Button",0x50010000,19,80,79,22,0x00000000
    CONTROL "Exit",IDC_BUTTON1004,"Button",0x50010000,117,80,83,22,0x00000000
END


dialog2.asm

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		user32.inc
include		kernel32.inc
includelib	user32.lib
includelib	kernel32.lib

DlgProc		proto		:DWORD,:DWORD,:DWORD,:DWORD

.data
Message		db		"Hello World!",0
Caption		db		"This is the text from the editbox",0

.data?
hInstance	HINSTANCE	?
buffer		db		512 dup(?)

.const
IDD_DLGBOX	equ		1001
IDC_EDITBOX	equ		1002
IDC_MESSAGE	equ		1003
IDC_EXIT	equ		1004
IDM_GETTEXT	equ		1005
IDM_CLEARTEXT	equ		1006
IDM_EXIT	equ		1007
ARIcon		equ		2000

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke DialogBoxParam, hInstance, IDD_DLGBOX, NULL, addr DlgProc, NULL
    invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg == WM_INITDIALOG
    invoke	LoadIcon,hInstance,ARIcon
    invoke	SendMessage, hWnd, WM_SETICON, 1, eax
    invoke GetDlgItem, hWnd,IDC_EDITBOX
    invoke SetFocus,eax
.elseif uMsg == WM_COMMAND
    mov	eax,wParam
    .if eax==IDC_MESSAGE
        invoke SetDlgItemText,hWnd,IDC_EDITBOX,ADDR Message
    .elseif eax==IDC_EXIT
        invoke	SendMessage, hWnd, WM_CLOSE, 0, 0
    .elseif eax==IDM_GETTEXT
        invoke GetDlgItemText,hWnd,IDC_EDITBOX,ADDR buffer,512
        invoke MessageBox,NULL,ADDR buffer,ADDR Caption,MB_OK
    .elseif eax==IDM_CLEARTEXT
        invoke SetDlgItemText,hWnd,IDC_EDITBOX,NULL
    .elseif eax==IDM_EXIT
        invoke	SendMessage, hWnd, WM_CLOSE, 0, 0
    .endif
.elseif	uMsg == WM_CLOSE
    invoke	EndDialog, hWnd, 0
.endif
    xor	eax,eax
    ret
DlgProc endp

end start


dialog2.rc

#define IDD_DLGBOX	1001
#define IDC_EDITBOX	1002
#define IDC_MESSAGE	1003
#define IDC_EXIT	1004
#define IDM_GETTEXT	1005
#define IDM_CLEARTEXT	1006
#define IDM_EXIT	1007
#define MyMenu		1008
#define ARIcon		2000

ARIcon ICON DISCARDABLE "favicon.ico"

MyMenu MENUEX DISCARDABLE
BEGIN
    POPUP "Menu",1009
    BEGIN
        MENUITEM "Get Text",IDM_GETTEXT
        MENUITEM "Clear Text",IDM_CLEARTEXT
        MENUITEM "",1010,0x00000800
        MENUITEM "Exit",IDM_EXIT
    END
END

IDD_DLGBOX DIALOGEX 0,0,221,127
CAPTION "Simple Dialog Box Program"
FONT 8,"MS Sans Serif"
MENU MyMenu
STYLE 0x10cc0800
EXSTYLE 0x00000000
BEGIN
    CONTROL "",IDC_EDITBOX,"Edit",0x50010080,30,22,158,20,0x00000200
    CONTROL "Say Hello",IDC_MESSAGE,"Button",0x50010000,19,80,79,22,0x00000000
    CONTROL "Exit",IDC_EXIT,"Button",0x50010000,117,80,83,22,0x00000000
END


dialogbox2.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly
   xmlns="urn:schemas-microsoft-com:asm.v1"
   manifestVersion="1.0">
<assemblyIdentity
    processorArchitecture="x86"
    version="5.1.0.0"
    type="win32"
    name="dialogbox2.exe"/>
    <description>Mx XP Program</description>
    <dependency>
    <dependentAssembly>
    <assemblyIdentity
         type="win32"
         name="Microsoft.Windows.Common-Controls"
         version="6.0.0.0"
         publicKeyToken="6595b64144ccf1df"
         language="*"
         processorArchitecture="x86"/>
    </dependentAssembly>
    </dependency>
</assembly>


simplekeygen.asm

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		kernel32.inc
include		user32.inc
includelib	kernel32.lib
includelib	user32.lib

DlgProc		proto		:DWORD,:DWORD,:DWORD,:DWORD

.data?
hInstance	HINSTANCE	?
NameBuffer	db		32 dup(?)
SerialBuffer	db		32 dup(?)

.const
IDD_KEYGEN	equ		1001
IDC_NAME	equ		1002
IDC_SERIAL	equ		1003
IDC_GENERATE	equ		1004
IDC_COPY	equ		1005
IDC_EXIT	equ		1006
ARIcon		equ		2001

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke DialogBoxParam, hInstance, IDD_KEYGEN, NULL, addr DlgProc, NULL
    invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg == WM_INITDIALOG
    invoke	LoadIcon,hInstance,ARIcon
    invoke	SendMessage,hWnd,WM_SETICON,1,eax
    invoke GetDlgItem,hWnd,IDC_NAME
    invoke SetFocus,eax
.elseif uMsg == WM_COMMAND
    mov	eax,wParam
    .if eax==IDC_GENERATE
        invoke GetDlgItemText,hWnd,IDC_NAME,addr NameBuffer,32
        call Generate
        invoke SetDlgItemText,hWnd,IDC_SERIAL,addr SerialBuffer
    .elseif eax==IDC_COPY
        invoke SendDlgItemMessage,hWnd,IDC_SERIAL,EM_SETSEL,0,-1
        invoke SendDlgItemMessage,hWnd,IDC_SERIAL,WM_COPY,0,0
    .elseif eax==IDC_EXIT
        invoke	SendMessage,hWnd,WM_CLOSE,0,0
    .endif
.elseif	uMsg == WM_CLOSE
    invoke	EndDialog,hWnd,0
.endif
    xor	eax,eax
    ret
DlgProc endp

Generate proc
invoke lstrlen, addr NameBuffer
test eax, eax
jle NOINPUT
mov ecx, eax
mov esi, offset NameBuffer
mov edi, offset SerialBuffer
@@:
    dec ecx
    mov dl, BYTE ptr [esi+ecx]
    mov BYTE ptr[edi], dl
    inc edi
    or ecx, ecx
    ja @b
NOINPUT:
    ret
Generate endp

end start


simplekeygen.rc

#define IDC_STATIC1007 1007
#define IDC_STATIC1008 1008
#define IDC_NAME 1002
#define IDC_SERIAL 1003
#define IDC_GENERATE 1004
#define IDC_COPY 1005
#define IDC_EXIT 1006
#define IDD_KEYGEN 1001
#define ARIcon 2001

ARIcon ICON DISCARDABLE "favicon.ico"

IDD_KEYGEN DIALOGEX 0,0,221,110
CAPTION "ARTeam Keygen"
FONT 8,"MS Sans Serif"
STYLE 0x10cc0800
EXSTYLE 0x00000000
BEGIN
    CONTROL "",IDC_NAME,"Edit",0x50010080,44,15,163,13,0x00000200
    CONTROL "",IDC_SERIAL,"Edit",0x50010880,44,43,163,13,0x00000200
    CONTROL "Generate",IDC_GENERATE,"Button",0x50010000,10,71,61,22,0x00000000
    CONTROL "Copy",IDC_COPY,"Button",0x50010000,80,71,61,22,0x00000000
    CONTROL "Exit",IDC_EXIT,"Button",0x50010000,150,71,61,22,0x00000000
    CONTROL "Name:",IDC_STATIC1007,"Static",0x50000200,13,15,21,13,0x00000000
    CONTROL "Serial:",IDC_STATIC1008,"Static",0x50000200,13,43,21,13,0x00000000
END


customkeygen.asm

.386
.model flat,stdcall
option casemap:none

include			windows.inc
include			kernel32.inc
include			user32.inc
include			ufmodapi.inc
includelib		kernel32.lib
includelib		user32.lib
includelib		ufmod.lib
includelib		winmm.lib

DlgProc			proto		:DWORD,:DWORD,:DWORD,:DWORD
FadeIn      		proto		:DWORD
FadeOut		    	proto		:DWORD
Generate		proto		:DWORD

.data
include chiptune.inc
xmSize equ $ - table
Hash1			dd		026h
Hash2			dd		034h
Hash3			dd		0ch
Hash4			dd		0eh
FormatControl		db		"%d-",0
EndFormatControl 	db		"%d",0
AboutTxt		db		"SuperCleaner 2.89 Keygen",10,13
            db		"~Coded by Goppit~",10,13
            db		"Release Date 23/02/06",0
AboutCap		db		"NFO",0

.data?
hInstance		HINSTANCE	?
Transparency		dd		?
NameBuffer		db		40 dup(?)
SerialBuffer		db		40 dup(?)
SerialSection		db		32 dup(?)

.const
IDD_KEYGEN		equ		1001
IDC_EXIT		equ		1002
IDC_COPY		equ		1003
IDC_ABOUT		equ		1004
IDC_NAME		equ		1005
IDC_SERIAL		equ		1006
ICON			equ		2001
LWA_ALPHA		equ		2
LWA_COLORKEY		equ		1
WS_EX_LAYERED		equ		80000h
DELAY_VALUE		equ		10

.code
start:
    invoke GetModuleHandle,NULL
    mov hInstance,eax
    invoke DialogBoxParam,hInstance,IDD_KEYGEN,NULL,addr DlgProc,NULL
    invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg == WM_INITDIALOG
        invoke GetWindowLong,hWnd,GWL_EXSTYLE
        or eax,WS_EX_LAYERED
        invoke SetWindowLong,hWnd,GWL_EXSTYLE,eax
        invoke SetWindowPos,hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE+SWP_NOSIZE
        invoke LoadIcon,hInstance,ICON
        invoke SendMessage,hWnd,WM_SETICON,1,eax
        invoke GetDlgItem,hWnd,IDC_NAME
        invoke SetFocus,eax
        invoke uFMOD_PlaySong,addr table,xmSize,XM_MEMORY
        invoke FadeIn,hWnd
    .elseif uMsg==WM_LBUTTONDOWN
        invoke SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam
    .elseif uMsg==WM_COMMAND
        mov eax,wParam
        .if ax==IDC_NAME
            shr eax,16
        .if ax==EN_CHANGE
            invoke Generate,hWnd
        .endif
    .elseif eax==IDC_ABOUT
        invoke MessageBox,hWnd,addr AboutTxt,addr AboutCap,MB_OK
    .elseif eax==IDC_COPY
        invoke SendDlgItemMessage,hWnd,IDC_SERIAL,EM_SETSEL,0,-1
        invoke SendDlgItemMessage,hWnd,IDC_SERIAL,WM_COPY,0,0
    .elseif eax==IDC_EXIT
        invoke	SendMessage,hWnd,WM_CLOSE,0,0
    .endif
    .elseif	uMsg== WM_CLOSE
        invoke FadeOut,hWnd
        invoke uFMOD_PlaySong,0,0,0
        invoke EndDialog,hWnd,0
    .endif
    xor eax,eax
    ret
DlgProc endp

FadeIn proc hWnd:HWND
    invoke ShowWindow,hWnd,SW_SHOW
    mov Transparency,75
@@:
    invoke SetLayeredWindowAttributes,hWnd,0,Transparency,LWA_ALPHA
    invoke Sleep,DELAY_VALUE
    add Transparency,5
    cmp Transparency,255
    jne @b
    ret
FadeIn endp

FadeOut proc hWnd:HWND
    mov Transparency,255
@@:
    invoke SetLayeredWindowAttributes,hWnd,0,Transparency,LWA_ALPHA
    invoke Sleep,DELAY_VALUE
    sub Transparency,5
    cmp Transparency,0
    jne @b
    ret
FadeOut endp

Generate proc hWnd:HWND
    invoke GetDlgItemText,hWnd,IDC_NAME,addr NameBuffer,40
    mov edi,offset NameBuffer
    invoke lstrlen,edi
    mov esi,eax
    xor ecx,ecx
    xor eax,eax
    test esi,esi
    jle NOINPUT
    mov edx,Hash1
@@:
    movsx ebx,byte ptr [eax+edi]
    add ebx,edx
    add ecx,ebx
    inc eax
    cmp eax,esi
    jl @b
    invoke wsprintf,addr SerialBuffer,addr FormatControl,ecx
    xor ecx,ecx
    mov eax,ecx
    mov edx,Hash2
@@:
    movsx ebx,byte ptr [eax+edi]
    imul ebx,edx
    add ecx,ebx
    inc eax
    cmp eax,esi
    jl @b
    invoke wsprintf,addr SerialSection,addr FormatControl,ecx
    invoke lstrcat,addr SerialBuffer,addr SerialSection
    xor ecx,ecx
    mov eax,ecx
    mov edx,Hash3
@@:
    movsx ebx,byte ptr [eax+edi]
    add ebx,edx
    add ecx,ebx
    inc eax
    cmp eax,esi
    jl @b
    invoke wsprintf,addr SerialSection,addr FormatControl,ecx
    invoke lstrcat,addr SerialBuffer,addr SerialSection
    xor ecx,ecx
    mov eax,ecx
    mov edx,Hash4
@@:
    movsx ebx,byte ptr [eax+edi]
    imul ebx,edx
    add ecx,ebx
    inc eax
    cmp eax,esi
    jl @b
    invoke wsprintf,addr SerialSection,addr EndFormatControl,ecx
    invoke lstrcat,addr SerialBuffer,addr SerialSection
    invoke SetDlgItemText,hWnd,IDC_SERIAL,addr SerialBuffer
    xor eax,eax
    ret
NOINPUT:
    invoke RtlZeroMemory,addr SerialBuffer,40
    invoke SetDlgItemText,hWnd,IDC_SERIAL,addr SerialBuffer
    xor eax,eax
    ret
Generate endp

end start


customkeygen.rc

#define IDC_COPY 1003
#define IDC_EXIT 1002
#define IDD_KEYGEN 1001
#define IDC_NAME 1005
#define IDC_SERIAL 1006
#define LOGO 2002
#define FAVICON 2001
#define IDC_IMAGE 1010
#define IDC_ABOUT 1004
#define IDD_ABOUT 1007

FAVICON ICON DISCARDABLE "favicon.ico"
LOGO BITMAP DISCARDABLE "ARTeam_Keygen2.bmp"

IDD_KEYGEN DIALOGEX 0,0,261,106
FONT 8,"MS Sans Serif"
STYLE 0x90000800
EXSTYLE 0x00000000
BEGIN
    CONTROL "#2002",IDC_IMAGE,"Static",0x5002020e,-7,-3,274,114,0x00000000
    CONTROL "About",IDC_ABOUT,"Button",0x54018000,30,80,47,17,0x00000000
    CONTROL "Copy",IDC_COPY,"Button",0x54018000,110,80,47,17,0x00000000
    CONTROL "Exit",IDC_EXIT,"Button",0x54018000,191,80,47,17,0x00000000
    CONTROL "",IDC_NAME,"Edit",0x50810081,71,9,164,13,0x00000000
    CONTROL "SuperCleaner 2.89 Keygen",IDC_SERIAL,"Edit",0x50810881,71,28,163,12,0x00000000
END

IDD_ABOUT DIALOGEX 0,0,151,103
FONT 8,"MS Sans Serif"
STYLE 0xd0000800
EXSTYLE 0x00000000
BEGIN
END


region.asm

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		user32.inc
include		kernel32.inc
include		gdi32.inc
includelib	user32.lib
includelib	kernel32.lib
includelib	gdi32.lib

DlgProc		proto		:DWORD,:DWORD,:DWORD,:DWORD

.data
caption		db		"About",0
message		db		"How to apply a complex",13,10,"region to a simple",13,10,"dialog box.",0

.data?
hInstance	dd		?
hResInfo	dd		?
hResData	dd		?
pRsrc		dd		?
sizeRsrc	dd		?
brush		dd		?

.code
start:
    invoke GetModuleHandle, NULL
    mov hInstance,eax
    invoke DialogBoxParam, hInstance, 1001, NULL, addr DlgProc, NULL
    invoke ExitProcess,0

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg == WM_INITDIALOG
        invoke SetWindowPos,hWnd, HWND_TOPMOST,0,0,0,0,SWP_NOACTIVATE + SWP_NOMOVE + SWP_NOSIZE
        invoke LoadBitmap,hInstance,2001
        invoke CreatePatternBrush,eax
        mov brush, eax
        invoke FindResource,hInstance,2002,RT_RCDATA
        mov hResInfo,eax
        invoke LoadResource,hInstance,hResInfo
        mov hResData, eax
        invoke SizeofResource,hInstance,hResInfo
        mov	sizeRsrc,eax
        invoke LockResource,hResData
        mov pRsrc, eax
        invoke ExtCreateRegion,NULL,sizeRsrc,pRsrc
        invoke SetWindowRgn,hWnd,eax,TRUE
    .elseif uMsg==WM_CTLCOLORDLG
        mov eax, brush
        ret
    .elseif uMsg==WM_COMMAND
        mov eax,wParam
        .if eax==1002
            invoke MessageBox,hWnd,addr message,addr caption,MB_ICONASTERISK
        .endif
    .elseif uMsg==WM_LBUTTONDBLCLK
        invoke SendMessage,hWnd,WM_CLOSE,0,0
    .elseif uMsg==WM_LBUTTONDOWN
        invoke SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,lParam
    .elseif	uMsg == WM_CLOSE
        invoke DeleteObject,brush
        invoke EndDialog, hWnd, 0
    .endif
    xor	eax,eax
    ret
DlgProc endp

end start


region.rc

#define IDD_DLG1001 1001
#define IDC_BUTTON1002 1002

2001 BITMAP DISCARDABLE "tweety78.bmp"
2002 RCDATA DISCARDABLE "region.rgn"

IDD_DLG1001 DIALOGEX 0,0,200,186
FONT 8,"MS Sans Serif"
STYLE 0x90000800
EXSTYLE 0x00000000
BEGIN
    CONTROL "?",IDC_BUTTON1002,"Button",0x50010000,163,132,10,10,0x00000000
END


offsetpatcher.asm

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		user32.inc
include		kernel32.inc
includelib	user32.lib
includelib	kernel32.lib

DlgProc		proto		:DWORD,:DWORD,:DWORD,:DWORD
Patch		proto		:DWORD
List		proto		:DWORD,:DWORD

.data
TargetName	db		"SuperCleaner.exe",0
BackupName	db		"SuperCleaner.exe.bak",0
Sequence	db		75h,41h,0E8h,7Ah
WBuffer		db		0EBh,41h,0E8h,7Ah
PatchOffset	dd		2347Fh
StartNfo	db		"Place in same folder as target and click APPLY",0
Backup		db		"Backup made",0
Success		db		"Target patched successfully",0
Already		db		"Target already patched!",0
OpenError	db		"Unable to open target file",0
ReadError	db		"Error reading from target file",0
WriteError	db		"Error writing to target file",0
Version		db		"Incorrect target file version",0
AboutTxt	db		"SuperCleaner 2.84 Patch",10,13
        db		"~Coded by Goppit~",10,13
        db		"Release Date 15/12/05",0
AboutCap	db		"NFO",0

.data?
hInstance	HINSTANCE	?
hTarget		HINSTANCE	?
RBuffer		dd		?
BytesRead	db		?
BytesWritten	db		?

.const
IDD_PATCH	equ		1001
IDC_LISTBOX	equ		1002
IDC_APPLY	equ		1003
IDC_ABOUT	equ		1004
IDC_EXIT	equ		1005
IDC_CHECKBOX	equ		1006
ARIcon		equ		2002

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke DialogBoxParam, hInstance, IDD_PATCH, NULL, addr DlgProc, NULL
    invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg==WM_INITDIALOG
        invoke LoadIcon,hInstance,ARIcon
        invoke SendMessage,hWnd,WM_SETICON,1,eax
        invoke List,hWnd,addr StartNfo
    .elseif uMsg==WM_COMMAND
        mov	eax,wParam
        .if eax==IDC_APPLY
            invoke Patch,hWnd
        .elseif eax==IDC_ABOUT
            invoke MessageBox,hWnd,addr AboutTxt,addr AboutCap,MB_ICONINFORMATION
        .elseif eax==IDC_EXIT
            invoke SendMessage,hWnd,WM_CLOSE,0,0
        .endif
    .elseif	uMsg==WM_CLOSE
        invoke	EndDialog,hWnd,0
    .endif
    xor	eax,eax
    ret
DlgProc endp

List proc hWnd:HWND, pMsg:DWORD
    invoke SendDlgItemMessage,hWnd,IDC_LISTBOX,LB_ADDSTRING,0,pMsg
    invoke SendDlgItemMessage,hWnd,IDC_LISTBOX,WM_VSCROLL,SB_BOTTOM,0
    Ret
List EndP

Patch proc hWnd:HWND
    invoke GetFileAttributes,addr TargetName
    .if eax!=FILE_ATTRIBUTE_NORMAL
        invoke SetFileAttributes,addr TargetName,FILE_ATTRIBUTE_NORMAL
    .endif
    invoke CreateFile,addr TargetName,\
                    GENERIC_READ+GENERIC_WRITE,\
                    FILE_SHARE_READ+FILE_SHARE_WRITE,\
                    NULL,\
                    OPEN_EXISTING,\
                    FILE_ATTRIBUTE_NORMAL,\
                    NULL
    .if eax!=INVALID_HANDLE_VALUE
        mov hTarget,eax
        invoke SendDlgItemMessage, hWnd, IDC_CHECKBOX, BM_GETCHECK, 0, 0
        .if eax==BST_CHECKED
            invoke CopyFile, addr TargetName, addr BackupName, TRUE
            invoke List,hWnd,addr Backup
        .endif
        invoke SetFilePointer,hTarget,PatchOffset,NULL,FILE_BEGIN
        invoke ReadFile,hTarget,addr RBuffer,4,addr BytesRead,NULL
        .if BytesRead==4
            mov eax,dword ptr [RBuffer]
            .if eax==dword ptr [Sequence]
                invoke SetFilePointer,hTarget,PatchOffset,NULL,FILE_BEGIN
                invoke WriteFile,hTarget,addr WBuffer,1,addr BytesWritten,NULL
                .if BytesWritten==1
                    invoke List,hWnd,addr Success
                .else
                    invoke List,hWnd,addr WriteError
                .endif
            .elseif eax==dword ptr [WBuffer]
                invoke List,hWnd,addr Already
            .else
                invoke List,hWnd,addr Version
            .endif
        .else
            invoke List,hWnd,addr ReadError
        .endif
    .else
        invoke List,hWnd,addr OpenError
    .endif
    invoke CloseHandle,hTarget
    Ret
Patch EndP

end start


SnRpatcher.asm

.386
.model flat,stdcall
option casemap:none

include		windows.inc
include		user32.inc
include		kernel32.inc
includelib	user32.lib
includelib	kernel32.lib

DlgProc		proto		:DWORD,:DWORD,:DWORD,:DWORD
List		proto		:DWORD,:DWORD
Patch		proto		:DWORD
Scan		proto

.data
TargetName	db		"SuperCleaner.exe",0
BackupName	db		"SuperCleaner.exe.bak",0
Sequence	db		75h,41h,0E8h,7Ah
SeqLen		db		4
WBuffer		db		0EBh,41h,0E8h,7Ah
PatchOffset	dd		2347Fh
StartNfo	db		"Place in same folder as target and click APPLY",0
Backup		db		"Backup made",0
Success		db		"Target patched successfully",0
Already		db		"Target already patched!",0
OpenError	db		"Unable to open target file",0
ReadError	db		"Error reading from target file",0
WriteError	db		"Error writing to target file",0
Version		db		"Incorrect target file version...",0
Searching	db		"Searching for byte sequence to patch...",0
NotFound	db		"Correct byte sequence not found",0
AboutTxt	db		"SuperCleaner 2.84 Patch",10,13
        db		"~Coded by Goppit~",10,13
        db		"Release Date 15/12/05",0
AboutCap	db		"NFO",0

.data?
hInstance	HINSTANCE	?
hTarget		HINSTANCE	?
hTargetMap	HINSTANCE	?
RBuffer		dd		?
BytesRead	db		?
BytesWritten	db		?
pMapView	dd		?
FileSize	dd		?
SearchOffset	dd		?

.const
IDD_PATCH	equ		1001
IDC_LISTBOX	equ		1002
IDC_APPLY	equ		1003
IDC_ABOUT	equ		1004
IDC_EXIT	equ		1005
IDC_CHECKBOX	equ		1006
ARIcon		equ		2002

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke DialogBoxParam, hInstance, IDD_PATCH, NULL, addr DlgProc, NULL
    invoke ExitProcess,eax

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg==WM_INITDIALOG
        invoke LoadIcon,hInstance,ARIcon
        invoke SendMessage,hWnd,WM_SETICON,1,eax
        invoke List,hWnd,addr StartNfo
    .elseif uMsg==WM_COMMAND
        mov	eax,wParam
        .if eax==IDC_APPLY
            invoke Patch,hWnd
        .elseif eax==IDC_ABOUT
            invoke MessageBox,hWnd,addr AboutTxt,addr AboutCap,MB_ICONINFORMATION
        .elseif eax==IDC_EXIT
            invoke SendMessage,hWnd,WM_CLOSE,0,0
        .endif
    .elseif	uMsg==WM_CLOSE
        invoke	EndDialog,hWnd,0
    .endif
    xor	eax,eax
    ret
DlgProc endp

List proc hWnd:HWND, pMsg:DWORD
    invoke SendDlgItemMessage,hWnd,IDC_LISTBOX,LB_ADDSTRING,0,pMsg
    invoke SendDlgItemMessage,hWnd,IDC_LISTBOX,WM_VSCROLL,SB_BOTTOM,0
    Ret
List EndP

Patch proc hWnd:HWND
    invoke GetFileAttributes,addr TargetName
    .if eax!=FILE_ATTRIBUTE_NORMAL
        invoke SetFileAttributes,addr TargetName,FILE_ATTRIBUTE_NORMAL
    .endif
    invoke CreateFile,addr TargetName,\
                    GENERIC_READ+GENERIC_WRITE,\
                    FILE_SHARE_READ+FILE_SHARE_WRITE,\
                    NULL,\
                    OPEN_EXISTING,\
                    FILE_ATTRIBUTE_NORMAL,\
                    NULL
    .if eax!=INVALID_HANDLE_VALUE
        mov hTarget,eax
        invoke SendDlgItemMessage, hWnd, IDC_CHECKBOX, BM_GETCHECK, 0, 0
        .if eax==BST_CHECKED
            invoke CopyFile, addr TargetName, addr BackupName, TRUE
            invoke List,hWnd,addr Backup
        .endif
        invoke SetFilePointer,hTarget,PatchOffset,NULL,FILE_BEGIN
        invoke ReadFile,hTarget,addr RBuffer,4,addr BytesRead,NULL
        .if BytesRead==4
            mov eax,dword ptr [RBuffer]
            .if eax==dword ptr [Sequence]
                invoke SetFilePointer,hTarget,PatchOffset,NULL,FILE_BEGIN
                jmp @WRITE
            .elseif eax==dword ptr [WBuffer]
                invoke List,hWnd,addr Already
            .else
                invoke List,hWnd,addr Version
                invoke List,hWnd,addr Searching
                invoke Scan
                .if eax==0
                    invoke List,hWnd,addr NotFound
                .else
                    invoke SetFilePointer,hTarget,eax,NULL,FILE_BEGIN
                    jmp @WRITE
                .endif
            .endif
        .else
            invoke List,hWnd,addr ReadError
        .endif
    .else
        invoke List,hWnd,addr OpenError
    .endif
    invoke CloseHandle,hTarget
    Ret

    @WRITE:
        invoke WriteFile,hTarget,addr WBuffer,1,addr BytesWritten,NULL
        .if BytesWritten==1
            invoke List,hWnd,addr Success
        .else
            invoke List,hWnd,addr WriteError
        .endif
        Ret
Patch EndP

Scan proc	;returns offset of byte sequence in eax
    LOCAL ReturnValue	:DWORD
    mov ReturnValue,0
    invoke GetFileSize,hTarget,NULL
    mov FileSize,eax
    invoke CreateFileMapping,hTarget,NULL,PAGE_READWRITE,0,0,NULL
    mov hTargetMap,eax
    invoke MapViewOfFile,eax,FILE_MAP_WRITE,0,0,0
    mov pMapView,eax
    mov edi,pMapView
    mov ecx,FileSize
    mov esi,offset Sequence
    xor eax,eax
    mov al,byte ptr [esi]

    @@:	repnz scasb
        cmp ecx,0
        jz @NOTFOUND
        push ecx
        push esi
        push edi
        dec edi
        mov ecx,4
        repz cmpsb
        cmp ecx,0
        jz @FOUND
        pop edi
        pop esi
        pop ecx
        jmp @b

    @FOUND:
        pop edi
        pop esi
        pop ecx
        inc ecx
        mov eax,FileSize
        sub eax,ecx
        mov ReturnValue,eax
        jmp @RETURN

    @NOTFOUND:
        mov ReturnValue,0
        jmp @RETURN

    @RETURN:
        invoke UnmapViewOfFile,pMapView
        invoke CloseHandle,hTargetMap
        mov eax,ReturnValue
        Ret
Scan EndP

end start


loader1.asm

.386
.model flat,stdcall
option casemap:none

include			windows.inc
include			kernel32.inc
include			user32.inc
includelib		kernel32.lib
includelib		user32.lib

.data?
StartupInfo		STARTUPINFO		<>
ProcessInfo		PROCESS_INFORMATION 	<>
BytesWritten 		dd			?

.data
TargetName		db			"SuperCleaner.exe",0
ProcError		db			"Error starting target process.",0
WriteError		db			"Error writing to target process",0
PatchByte		db			0EBh
PatchVA			dd			0042374Fh

.code
start:
    invoke CreateProcess,addr TargetName,0,0,0,0,CREATE_SUSPENDED,0,0,addr StartupInfo,addr ProcessInfo
    .if eax==0
        invoke MessageBox,NULL,addr ProcError,NULL,MB_ICONERROR
    .else
        invoke WriteProcessMemory,ProcessInfo.hProcess,PatchVA,addr PatchByte,1,addr BytesWritten
        .if BytesWritten!=1
            invoke MessageBox,NULL,addr WriteError,NULL,MB_ICONERROR
            invoke TerminateProcess,ProcessInfo.hProcess,0
        .else
            invoke ResumeThread,ProcessInfo.hThread
        .endif
    .endif
    invoke ExitProcess,NULL
end start


loader1.rc

#define ARIcon 2002

ARIcon ICON DISCARDABLE "favicon.ico"


loader2.asm

.386
.model flat,stdcall
option casemap:none

include			windows.inc
include			kernel32.inc
include			user32.inc
includelib		kernel32.lib
includelib		user32.lib

.data?
ThreadContext		CONTEXT			<>
StartupInfo		STARTUPINFO		<>
ProcessInfo		PROCESS_INFORMATION	<>
StopBuffer		dw			?
ReadBuffer		dd			?
Buffer			dd			?

.data
TargetName		db			"SuperCleaner.exe",0
ProcError		db			"Error starting target process. Is loader in same folder as target!",0
ReadError		db			"Error reading from target process",0
Version			db			"Incorrect target file version!",0
WriteError		db			"Error writing to target process",0
ContextError		db 			"GetThreadContext failed. Target process terminated?",0
Sequence		db			75h,41h,0E8h,0BAh
StopVA			dd			004863ECh
OEPBytes		db			0E9h,0E9h
StopCode		db			0EBh,0FEh
PatchVA			dd			0042374Fh
PatchByte		db			0EBh

.code
start:
    invoke CreateProcess,addr TargetName,0,0,0,0,CREATE_SUSPENDED,0,0,addr StartupInfo,addr ProcessInfo
    .if eax==0
        invoke MessageBox,0,addr ProcError,0,0
    .else
        invoke ReadProcessMemory,ProcessInfo.hProcess,StopVA,addr StopBuffer,2,addr Buffer
        mov ax,word ptr [StopBuffer]
        .if ax==word ptr [OEPBytes]
            invoke WriteProcessMemory,ProcessInfo.hProcess,StopVA,addr StopCode,2,addr Buffer
            invoke ResumeThread,ProcessInfo.hThread
            invoke Sleep,10
            mov ThreadContext.ContextFlags,CONTEXT_FULL
@@:
            invoke GetThreadContext,ProcessInfo.hThread,addr ThreadContext
            .if eax==0
                invoke MessageBox,0,addr ContextError,0,0
                jmp @Error
            .else
                mov eax,StopVA
                .if eax==ThreadContext.regEip
                    invoke SuspendThread,ProcessInfo.hThread
                    invoke ReadProcessMemory,ProcessInfo.hProcess,PatchVA,addr ReadBuffer,4,addr Buffer
                    mov eax,dword ptr [ReadBuffer]
                    .if eax==dword ptr [Sequence]
                        invoke WriteProcessMemory,ProcessInfo.hProcess,StopVA,addr OEPBytes,2,addr Buffer
                        invoke WriteProcessMemory,ProcessInfo.hProcess,PatchVA,addr PatchByte,1,addr Buffer
                        invoke ResumeThread,ProcessInfo.hThread
                        jmp @Exit
                    .else
                        invoke MessageBox,0,addr Version,0,0
                        jmp @Error
                    .endif
                .else
                    invoke Sleep,10
                    jmp @b
                .endif
            .endif
        .else
            invoke MessageBox,0,addr Version,0,0
        .endif
@Error:
        invoke TerminateProcess,ProcessInfo.hProcess,0
    .endif
@Exit:
    invoke ExitProcess,NULL
end start


validate.asm

.386
.model	flat, stdcall
option	casemap :none

include		windows.inc
include		user32.inc
include		kernel32.inc
include		comdlg32.inc
includelib	user32.lib
includelib	kernel32.lib
includelib	comdlg32.lib

DlgProc		PROTO		:DWORD,:DWORD,:DWORD,:DWORD
Validate	PROTO		:DWORD

.data
strFilter	db		"Executable Files (*.exe, *.dll)",0,
                "*.exe;*.dll",0,"All Files",0,"*.*",0,0
OpenError	db		"Unable to open target file",0
MsgBoxCap	db		"Results",0
Invalid		db		"This is not a valid PE file!!",0
Valid		db		"This is a valid PE file!!",0

.data?
ofn		OPENFILENAME	<>
hInstance	HINSTANCE	?
hTarget		dd		?
hMapping	dd		?
pMapping	dd		?
TargetName	db		512 dup(?)

.const
IDD_MAIN	equ		1001
IDC_TARGET	equ		1003
IDC_OPEN	equ		1004
IDC_GO 		equ		1005
IDC_EXIT 	equ		1006
ARIcon		equ		2001

.code
start:
    invoke	GetModuleHandle, NULL
    mov	hInstance, eax
    invoke	DialogBoxParam,hInstance,IDD_MAIN,0,addr DlgProc,0
    invoke	ExitProcess, eax

DlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    mov	eax,uMsg
    .if	eax==WM_INITDIALOG
        invoke	LoadIcon,hInstance,2001
        invoke	SendMessage,hWin,WM_SETICON,1,eax
    .elseif eax==WM_COMMAND
        mov	eax,wParam
        .if	eax==IDC_OPEN
            mov ofn.lStructSize,SIZEOF ofn
            mov ofn.lpstrFilter,offset strFilter
            mov ofn.lpstrFile,offset TargetName
            mov ofn.nMaxFile,512
            mov ofn.Flags,OFN_FILEMUSTEXIST+OFN_PATHMUSTEXIST+\
                        OFN_LONGNAMES+OFN_EXPLORER+OFN_HIDEREADONLY
            invoke GetOpenFileName,addr ofn
            .if eax==TRUE
                invoke SetDlgItemText,hWin,IDC_TARGET,addr TargetName
                invoke RtlZeroMemory,addr TargetName,512
            .endif
        .elseif eax==IDC_GO
            invoke GetDlgItemText,hWin,IDC_TARGET,addr TargetName,512
            invoke lstrlen,addr TargetName
            .if eax!=0
                invoke Validate,addr TargetName
            .endif
        .elseif eax==IDC_EXIT
            invoke SendMessage,hWin,WM_CLOSE,0,0
        .endif
    .elseif	eax==WM_CLOSE
        invoke	EndDialog,hWin,0
    .endif
    xor	eax,eax
    ret
DlgProc endp

Validate proc FileName:DWORD
    invoke CreateFile,FileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
    .if eax!=INVALID_HANDLE_VALUE
        mov hTarget,eax
        invoke CreateFileMapping,eax,0,PAGE_READ,0,0,0
        mov hMapping,eax
        invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0
        mov pMapping,eax
        .if [eax.IMAGE_DOS_HEADER.e_magic]==IMAGE_DOS_SIGNATURE
            add eax,[eax.IMAGE_DOS_HEADER.e_lfanew]
            .if [eax.IMAGE_NT_HEADERS.Signature]==IMAGE_NT_SIGNATURE
                invoke MessageBox,0,addr Valid,addr MsgBoxCap,MB_ICONASTERISK
            .endif
        .else
            invoke MessageBox,0,addr Invalid,addr MsgBoxCap,MB_ICONASTERISK
        .endif
        invoke UnmapViewOfFile,pMapping
        invoke CloseHandle,hMapping
        invoke CloseHandle,hTarget
    .else
        invoke MessageBox,0,addr OpenError,0,0
    .endif
    xor eax,eax
    Ret
Validate EndP

end start


validate.rc

#define IDD_DLG1001 1001
#define IDC_STATIC1002 1002
#define IDC_OPEN 1004
#define ARIcon 2001
#define IDC_TARGET 1003
#define IDC_EXIT 1006
#define IDC_GO 1005

ARIcon ICON DISCARDABLE "C:/Code Library/Projects/asm tut/PEValidate/favicon.ico"
1 24 DISCARDABLE "C:/Code Library/Projects/asm tut/PEValidate/XPVisualStyle.xml"

IDD_DLG1001 DIALOGEX 0,0,217,71
CAPTION "PE Validator"
FONT 8,"MS Sans Serif"
STYLE 0x10cc0800
EXSTYLE 0x00000000
BEGIN
    CONTROL "Target:",IDC_STATIC1002,"Static",0x50000201,10,15,24,15,0x00000000
    CONTROL "",IDC_TARGET,"Edit",0x50010080,37,15,144,16,0x00000200
    CONTROL ". . .",IDC_OPEN,"Button",0x50010000,187,15,17,16,0x00000000
    CONTROL "GO!",IDC_GO,"Button",0x50010000,37,43,51,16,0x00000000
    CONTROL "EXIT",IDC_EXIT,"Button",0x50010000,130,43,51,16,0x00000000
END


AddSection.asm

.386
.model	flat, stdcall
option	casemap :none

include		windows.inc
include		user32.inc
include		kernel32.inc
include		comdlg32.inc
includelib	user32.lib
includelib	kernel32.lib
includelib	comdlg32.lib

DlgProc			PROTO		:DWORD,:DWORD,:DWORD,:DWORD
Validate		PROTO		:DWORD
AddSection		PROTO		:DWORD

.data
strFilter		db			"Executable Files (*.exe, *.dll)",0,
                            "*.exe;*.dll",0,"All Files",0,"*.*",0,0
OpenError		db			"Unable to open target file",0
Invalid			db			"This is not a valid PE file!!",0
Success			db			"New section added successfully",0
NoSpace			db			"No space for new Section Header",0
Backup			db			".bak",0

;*********VALUES REQUIRED TO CONSTRUCT NEW SECTION*************
NewSecName		dd			"WEN."
NewSecSize		dd			100h
NewSecChar		dd			0E0000060h
KeepBkup		db			TRUE

.data?
ofn				OPENFILENAME	<>
hInstance		HINSTANCE	?
TargetName		db			512 dup(?)
hTarget			dd			?
hMapping		dd			?
pMapping		dd			?
pPEHeader		dd			?
Buffer			db			512 dup (?)

.const
IDD_MAIN		equ			1001
IDC_TARGET		equ			1003
IDC_BROWSE		equ			1004
IDC_GO 			equ			1005
IDC_EXIT 		equ			1006
ARIcon			equ			2001

.code
start:
    invoke	GetModuleHandle, NULL
    mov	hInstance, eax
    invoke	DialogBoxParam,hInstance,IDD_MAIN,0,addr DlgProc,0
    invoke	ExitProcess, eax

DlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    mov	eax,uMsg
    .if	eax==WM_INITDIALOG
        invoke	LoadIcon,hInstance,2001
        invoke	SendMessage,hWin,WM_SETICON,1,eax
    .elseif eax==WM_COMMAND
        mov	eax,wParam
        .if	eax==IDC_BROWSE
            mov ofn.lStructSize,SIZEOF ofn
            mov ofn.lpstrFilter,offset strFilter
            mov ofn.lpstrFile,offset TargetName
            mov ofn.nMaxFile,512
            mov ofn.Flags,OFN_FILEMUSTEXIST+OFN_PATHMUSTEXIST+\
                        OFN_LONGNAMES+OFN_EXPLORER+OFN_HIDEREADONLY
            invoke GetOpenFileName,addr ofn
            .if eax==TRUE
                invoke SetDlgItemText,hWin,IDC_TARGET,addr TargetName
                invoke RtlZeroMemory,addr TargetName,512
            .endif
        .elseif eax==IDC_GO
            invoke GetDlgItemText,hWin,IDC_TARGET,addr TargetName,512
            invoke lstrlen,addr TargetName
            .if eax!=0
                invoke Validate,addr TargetName
                .if eax==1
                    invoke AddSection,addr TargetName
                    .if eax==1
                        invoke MessageBox,hWin,addr Success,0,0
                    .elseif eax==-1
                        invoke MessageBox,hWin,addr NoSpace,0,0
                    .endif
                .elseif eax==-1
                    invoke MessageBox,hWin,addr Invalid,0,0
                .endif
            .endif
        .elseif eax==IDC_EXIT
            invoke SendMessage,hWin,WM_CLOSE,0,0
        .endif
    .elseif	eax==WM_CLOSE
        invoke	EndDialog,hWin,0
    .endif
    xor	eax,eax
    ret
DlgProc endp

Validate proc FileName:DWORD

;**************************************************************************************
; TAKES POINTER TO TARGET NAME, RETURNS 1 IF VALID, -1 IF NOT, 0 IF UNABLE TO OPEN FILE
;**************************************************************************************

LOCAL ReturnValue:DWORD
    invoke CreateFile,FileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
    .if eax!=INVALID_HANDLE_VALUE
        mov hTarget,eax
        invoke CreateFileMapping,eax,0,PAGE_READONLY,0,0,0
        mov hMapping,eax
        invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0
        mov pMapping,eax
        .if [eax.IMAGE_DOS_HEADER.e_magic]==IMAGE_DOS_SIGNATURE
            add eax,[eax.IMAGE_DOS_HEADER.e_lfanew]
            .if [eax.IMAGE_NT_HEADERS.Signature]==IMAGE_NT_SIGNATURE
                mov ReturnValue,1
                jmp @EXIT
            .endif
        .else
            mov ReturnValue,-1
            jmp @EXIT
        .endif
@EXIT:
        invoke UnmapViewOfFile,pMapping
        invoke CloseHandle,hMapping
        invoke CloseHandle,hTarget
        mov eax,ReturnValue
        ret
    .else
        invoke MessageBox,0,addr OpenError,0,0
        xor eax,eax
        Ret
    .endif
Validate EndP

AddSection proc FileName:DWORD

;*********************************************************************************************
; TAKES POINTER TO TARGET NAME, RETURNS 1 IF SUCCESS, -1 IF NO SPACE, 0 IF UNABLE TO OPEN FILE
;*********************************************************************************************

LOCAL ReturnValue:DWORD
;*******MAKE BACKUP***************************************************
        invoke lstrcpy,addr Buffer,FileName
        invoke lstrcat,addr Buffer,addr Backup
        invoke CopyFile,FileName,addr Buffer,TRUE
;*******OPEN FILE MAP IN AND INCREASE SIZE****************************
    invoke CreateFile,FileName,GENERIC_READ+GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
    .if eax!=INVALID_HANDLE_VALUE
        mov hTarget,eax
        invoke GetFileSize,eax,0
        add eax,NewSecSize
        invoke CreateFileMapping,hTarget,0,PAGE_READWRITE,0,eax,0
        mov hMapping,eax
        invoke MapViewOfFile,eax,FILE_MAP_ALL_ACCESS,0,0,0
        mov pMapping,eax
;******CHECK ENOUGH ROOM FOR NEW ISH IN SECTION TABLE******************
        add eax,[eax.IMAGE_DOS_HEADER.e_lfanew]	;pointer to PE header in eax
        mov pPEHeader,eax
        movzx esi,[eax.IMAGE_NT_HEADERS.FileHeader.NumberOfSections];no of sections in ecx
        inc esi									;add 1
        imul esi,esi,40							;calc size of all ISHs
        add esi,0F8h							;add above to offset of sec table from PE header
        mov ebx,pMapping
        add esi,[ebx.IMAGE_DOS_HEADER.e_lfanew]	;add above to offset of PE header from beginning of file
        .if esi<[eax.IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeaders]	;cmp offset of end of new ISH to offset of first section
;*******IF SO FILL NEW ISH**********************************************
            add esi,ebx							;make above offset into a pointer by adding base address
            sub esi,40
            mov edi,esi							;pointer to start of new ISH in edi
            sub esi,40							;pointer to start of last ISH in esi
            mov eax,NewSecName
            mov dword ptr [edi.IMAGE_SECTION_HEADER.Name1],eax	;write in Name1
            mov eax,NewSecSize
            mov [edi.IMAGE_SECTION_HEADER.Misc.VirtualSize],eax	;write in virtual size
            xor edx,edx											;align raw size of new sec
            mov ebx,pPEHeader
            mov ecx,[ebx.IMAGE_NT_HEADERS.OptionalHeader.FileAlignment]
            div ecx				;divides file alignment by raw size, quotient into eax, remainder into edx
            .if edx!=0			;if no remainder, ie already aligned then multiply back up
                inc eax			;if there is remainder, ie not aligned, then
            .endif				;add 1 to quotient and multiply back up (rounds up to alignment value)
            mul ecx
            mov [edi.IMAGE_SECTION_HEADER.SizeOfRawData],eax	;write in raw size
            mov eax,[esi.IMAGE_SECTION_HEADER.VirtualAddress]	;get RVA of last section
            add eax,[esi.IMAGE_SECTION_HEADER.Misc.VirtualSize]	;add virt size of last sec to get RVA of new sec
            xor edx,edx											;align RVA of new sec
            mov ecx,[ebx.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment]
            div ecx
            .if edx!=0
                inc eax
            .endif
            mul ecx
            mov [edi.IMAGE_SECTION_HEADER.VirtualAddress],eax	;write in RVA of new sec
            mov eax,[esi.IMAGE_SECTION_HEADER.PointerToRawData]	;get raw offset of last sec
            add eax,[esi.IMAGE_SECTION_HEADER.SizeOfRawData]	;add raw size
            xor edx,edx											;align raw offset
            mov ecx,[ebx.IMAGE_NT_HEADERS.OptionalHeader.FileAlignment]
            div ecx
            .if edx!=0
                inc eax
            .endif
            mul ecx
            mov [edi.IMAGE_SECTION_HEADER.PointerToRawData],eax	;write in raw offset of new sec
            mov edx,NewSecChar
            mov [edi.IMAGE_SECTION_HEADER.Characteristics],edx	;write in new sec characteristics
;*******UPDATE PE HEADER***********************************************
            mov eax,[edi.IMAGE_SECTION_HEADER.Misc.VirtualSize]	;get new sec virt size
            xor edx,edx											;align to section alignment
            mov ecx,[ebx.IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment]
            div ecx
            .if edx!=0
                inc eax
            .endif
            mul ecx
            add eax,[edi.IMAGE_SECTION_HEADER.VirtualAddress]	;add RVA where new section begins in memory
            mov [ebx.IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage],eax	;write new size of image
            inc [ebx.IMAGE_NT_HEADERS.FileHeader.NumberOfSections];add 1 to no of sections
            mov ReturnValue,1
            jmp @EXIT
        .else
;*******IF NO SPACE THEN RESTORE FROM BACKUP AND DELETE BACKUP**********
            invoke UnmapViewOfFile,pMapping
            invoke CloseHandle,hMapping
            invoke CloseHandle,hTarget
            invoke DeleteFile,FileName
            invoke CopyFile,addr Buffer,FileName,FALSE
            invoke DeleteFile,addr Buffer
            mov ReturnValue,-1
            jmp @EXIT
        .endif
@EXIT:
;*******DECIDE WHETHER TO KEEP BACKUP AFTER SUCCESS*********************
        invoke UnmapViewOfFile,pMapping
        invoke CloseHandle,hMapping
        invoke CloseHandle,hTarget
        .if KeepBkup==FALSE
            invoke DeleteFile,addr Buffer
        .endif
        mov eax,ReturnValue
        ret
    .else
        invoke MessageBox,0,addr OpenError,0,0
        xor eax,eax
        Ret
    .endif
    Ret
AddSection EndP

end start


AddSection.rc

#define IDD_DLG1001 1001
#define IDC_STATIC1002 1002
#define ARIcon 2001
#define IDC_TARGET 1003
#define IDC_EXIT 1006
#define IDC_GO 1005
#define IDC_BROWSE 1004

ARIcon ICON DISCARDABLE "C:/Code Library/Projects/asm tut/PEValidate/favicon.ico"
1 24 DISCARDABLE "C:/Code Library/Projects/asm tut/PEValidate/XPVisualStyle.xml"

IDD_DLG1001 DIALOGEX 0,0,217,71
CAPTION "AddSection"
FONT 8,"MS Sans Serif"
STYLE 0x10cc0800
EXSTYLE 0x00000000
BEGIN
    CONTROL "Target:",IDC_STATIC1002,"Static",0x50000201,10,15,24,15,0x00000000
    CONTROL "",IDC_TARGET,"Edit",0x50010080,37,15,144,16,0x00000200
    CONTROL ". . .",IDC_BROWSE,"Button",0x50010000,187,15,17,16,0x00000000
    CONTROL "GO!",IDC_GO,"Button",0x50010000,37,43,51,16,0x00000000
    CONTROL "EXIT",IDC_EXIT,"Button",0x50010000,130,43,51,16,0x00000000
END


DLLskeleton.asm

.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data

.data?

.code

DLLEntry proc hInstDLL:DWORD, reason:DWORD, unused:DWORD

    .if reason == DLL_PROCESS_ATTACH	; initialisation code for when DLL is loaded
        mov eax,TRUE			; put TRUE in EAX to continue loading the DLL
    .elseif reason == DLL_PROCESS_DETACH
        ; exit code for when DLL unloaded
    .elseif reason == DLL_THREAD_ATTACH
        ; code for when a thread is created in the process
    .elseif reason == DLL_THREAD_DETACH
        ; code for when a thread is destroyed in the process
    .endif
    Ret

DLLEntry Endp

ExpFunc1 proc
    ; code for first exported function here
    Ret
ExpFunc1 EndP

end DLLEntry


MyDLL.asm

.386
.model flat, stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

.data
Caption db "Exported Function",0
Text1	db "You called function 1 from MyDLL",0
Text2	db "You called function 2 from MyDLL",0

.code

DLLEntry proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
    mov eax,TRUE			; no conditions needed here
    Ret				; no need to save hInstDLL
DLLEntry Endp				; simply put TRUE in EAX to continue loading the DLL

_ExpFunc1 proc
    invoke MessageBox,0,addr Text1,addr Caption,MB_OK
    Ret
_ExpFunc1 EndP

_ExpFunc2 proc
    invoke MessageBox,0,addr Text2,addr Caption,MB_OK
    Ret
_ExpFunc2 EndP

end DLLEntry


Implicit.asm

.386
.model flat, stdcall
option casemap:none

include windows.inc
include kernel32.inc
include MyDLL.inc
includelib kernel32.lib
includelib MyDLL.lib

.code
start:
call _ExpFunc1
call _ExpFunc2	; note both functions are accessible by name because we have lib and inc files
invoke ExitProcess,0
end start


Explicit.asm

.386
.model flat, stdcall
option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
LibName		db	"MyDLL",0
ProcName	db	"_ExpFunc1",0

.data?
hInstLib	HINSTANCE	?

.code
start:
invoke LoadLibrary,addr LibName
mov hInstLib,eax
invoke GetProcAddress,eax,addr ProcName
call eax
invoke GetProcAddress,hInstLib,2
call eax
invoke FreeLibrary,hInstLib
invoke ExitProcess,0
end start


tELock.asm

; #########################################################################
; tELock.asm : Plugin for ImpREC to find tELock0.92x real API in its wrapped code
;
; Very quick and simple example to get all values in "push [API]; ... garbage ... ;ret"
; Note that this example is not a tracer but just an opcode checker.
; #########################################################################

.386
.model flat, stdcall
option casemap :none

include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

.code

LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
    ret
LibMain Endp

; ##########################################################################
; Parameters for Exported function:
;

; <hFileMap>    : HANDLE of the mapped file
; <dwSizeMap>   : Size of that mapped file
; <dwTimeOut>   : TimeOut of ImpREC in Options
; <dwToTrace>   : Pointer to trace (in VA)
; <dwExactCall> : EIP of the exact call (in VA)
;
; Returned value (in eax):
;

; Use a value greater or equal to 200. It will be shown by ImpREC if no output were created
; ##########################################################################

Trace proc hFileMap:DWORD, dwSizeMap:DWORD, dwTimeOut:DWORD, dwToTrace:DWORD, dwExactCall:DWORD

LOCAL dwPtrOutput : DWORD
LOCAL dwErrorCode : DWORD

    push ebx					; save ebx before we start
    invoke MapViewOfFile,hFileMap,FILE_MAP_READ + FILE_MAP_WRITE,0,0,0
    test eax,eax				; if API returns zero...
    jnz map_ok
    mov eax,201					; ...can't map view so put error code 201 in eax...
    pop ebx						; ...restore ebx...
    ret							; and return

map_ok:
    mov dwPtrOutput,eax			; if successful get pointer to file view in dwPtrOutput
    cmp dwSizeMap,4				; ensure size of view >=4
    jae map_ok2
    mov dwErrorCode,203			; otherwise Invalid map size so put error code 203 in eax...
    jmp end2					; ...and jump to close handles and return

map_ok2:
    mov ebx,dwToTrace			; put Pointer to trace in ebx
    invoke IsBadReadPtr,ebx,4	; check if the pointer is a valid address
    test eax,eax				; test if result is zero (valid) or non-zero (invalid)
    jz ptr_ok1
    mov dwErrorCode,205			; if Invalid pointer put error code 205 in eax
    jmp end2					; and jump to close handles and return

ptr_ok1:
    cmp byte ptr[ebx],0FFh		; if pointer OK then check if bytes pointed at include 0FFh...
    jnz end_ok
    cmp byte ptr[ebx+1],035h	; ...and 035h (opcodes for push [XXXXXXXX])
    jnz end_ok
    mov ebx,[ebx+2]				; Get this [XXXXXXXX] API address in ebx
    invoke IsBadReadPtr,ebx,4	; Check if address is valid
    test eax,eax
    jz ptr_ok2
    mov dwErrorCode,205			; If not, put Invalid pointer error code in eax...
    jmp end2					; ...and jmp to close handles and return

ptr_ok2:						; If API address valid then write the found pointer into the mapped file
    mov ebx,[ebx]				; swap the memory address in ebx for the value at that address
    mov eax,dwPtrOutput			; put pointer to file view in eax
    mov [eax],ebx				; write bytes in ebx into file view (pointed at by eax)

end_ok:
    mov dwErrorCode, 200		; If all is well then put success code in eax and proceed to close handles...

end2:
    invoke UnmapViewOfFile,dwPtrOutput ; Unmap the view
    invoke CloseHandle,hFileMap;       ; Close the handle of the mapped file
    mov eax,dwErrorCode                ; Set error code as returned value
    pop ebx
    ret

Trace endp

End LibMain


tELock.def

LIBRARY tELock
EXPORTS Trace


IsDebug.asm

.586p
.model flat, stdcall  ; 32 bit memory model
option scoped         ; local labels are enabled, global labels inside
                      ; PROC should be defined with double colons (LABEL::)
option casemap :none  ; case sensitive

DlgDumpProc		proto :DWORD,:DWORD,:DWORD,:DWORD
DlgOptionProc   proto :DWORD,:DWORD,:DWORD,:DWORD

include windows.inc
include kernel32.inc
include user32.inc
include ..\plugin.inc
include masm32.inc
include comdlg32.inc

includelib kernel32.lib
includelib user32.lib
includelib ..\ollydbg.lib
includelib masm32.lib
includelib comdlg32.lib

    ;

    ; literal string MACRO
    ;

      literal MACRO quoted_text:VARARG
        LOCAL local_text
        .data
          local_text db quoted_text,0
        .code
        EXITM <local_text>
      ENDM

      CTEXT MACRO quoted_text:VARARG
        EXITM <offset literal(quoted_text)>
      ENDM

      m2m MACRO M1, M2
        push M2
        pop  M1
      ENDM

      return MACRO arg
        mov eax, arg
        ret
      ENDM

MAXSIZE equ 260

;Dumper.dlg
IDD_DUMP								equ 1000
IDC_BTCANCEL							equ 1001
IDC_BTDUMP								equ 1002
IDC_EDTSIZE								equ 1003
IDC_STC1								equ 1004
IDC_EDTOFFSET							equ 1005
IDC_STC2								equ 1006

;Res\Params.dlg
IDD_OPTION								equ 1001
IDC_GRP1								equ 1003
IDC_CHK1								equ 1001
IDC_STC3								equ 1002
IDC_SLEEPTIME							equ 1004
IDC_STC4								equ 1005
IDC_BTN1								equ 1006
IDC_BTN2								equ 1007

;IsDebug.rc

.data
the_byte            db 1
null_byte           db 0
ofn                 OPENFILENAME <>
FilterString        db "Bin Files",0    ;dont insert between
bin_extend          db "*.bin",0,0      ;here
template            db "%d",0
str_idb_Autoload    db "idb_Autoload",0
str_idb_Sleeptime   db "idb_Sleeptime",0

.data?

hinst               HINSTANCE        ?  ; DLL instance
hwmain              HWND             ?  ; Handle of main OllyDbg window
textbuffer          db 512 dup(?)
byte_location       dd ?
SizeWritten         dd ?
hFileWrite          dd ?
hMemory_code        dd ?
pMemory_code        dd ?
SVWinClass          db 32 dup(?)
svthreadid          dd ?
sleep_time          dd ?
auto_load           dd ?
dw_buffer           dd ?

.code

; Entry point into a plugin DLL. Many system calls require DLL instance
; which is passed to DllEntryPoint() as one of parameters. Remember it.
; Preferrable way is to place initializations into ODBG_Plugininit() and
; cleanup in ODBG_Plugindestroy().

DllEntryPoint proc hi:HINSTANCE, reason:dword, res:dword
        .IF reason == DLL_PROCESS_ATTACH
            m2m     hinst, hi               ; Mark plugin instance
        .ENDIF
        return  1                           ; Report success
DllEntryPoint endp

; ODBG_Plugindata() is a "must" for valid OllyDbg plugin. It must fill in
; plugin name and return version of plugin interface. If function is absent,
; or version is not compatible, plugin will be not installed. Short name
; identifies it in the Plugins menu. This name is max. 31 alphanumerical

; characters or spaces + terminating '\0' long. To keep life easy for users,
; this name should be descriptive and correlate with the name of DLL.

ODBG_Plugindata proc C shortname:ptr byte
        invoke  lstrcpy, shortname, CTEXT("IsDebugPresent")      ; Name of plugin
        return  PLUGIN_VERSION;
ODBG_Plugindata endp

; OllyDbg calls this obligatory function once during startup. Place all
; one-time initializations here. If all resources are successfully allocated,

; function must return 0. On error, it must free partially allocated resources
; and return -1, in this case plugin will be removed. Parameter ollydbgversion
; is the version of OllyDbg, use it to assure that it is compatible with your
; plugin; hw is the handle of main OllyDbg window, keep it if necessary.
; Parameter features is reserved for future extentions, do not use it.
ODBG_Plugininit proc C ollydbgversion:dword, hw:HWND, features:ptr dword

        ; Check that version of OllyDbg is correct.
        .IF ollydbgversion < PLUGIN_VERSION
            jmp     @@bad_exit
        .ENDIF
        invoke  Addtolist, 0, 0, CTEXT("IsDebugPresent plugin v1.4 (SV 2oo3)")

        ; Keep handle of main OllyDbg window. This handle is necessary, for example,
        ; to display message box.
        m2m     hwmain, hw
        return  0

@@bad_exit:
        return  -1
ODBG_Plugininit endp

; OllyDbg calls this optional function once on exit. At this moment, all MDI
; windows created by plugin are already destroyed (and received WM_DESTROY
; messages). Function must free all internally allocated resources, like
; window classes, files, memory and so on.
ODBG_Plugindestroy proc C
        invoke Unregisterpluginclass,addr SVWinClass
        ret
ODBG_Plugindestroy endp

; Function is called when user opens new or restarts current application.
; Plugin should reset internal variables and data structures to initial state.
ODBG_Pluginreset proc C
       invoke Pluginreadintfromini,hinst,addr str_idb_Autoload,0    ;Auto ?
       .if (eax!=0)
           invoke Pluginreadintfromini,hinst,addr str_idb_Sleeptime,1000
           mov sleep_time,eax   ;save time value

           lea eax,svthread
           invoke CreateThread,NULL,NULL,eax,NULL,NULL,svthreadid
       .endif
       ret

svthread:
       invoke Sleep,sleep_time                 ;Wait a little ;)
       invoke Plugingetvalue,VAL_PROCESSID     ;is something loaded ??
       .if (eax!=0)
;           invoke SetWindowText,hwmain,CTEXT("0llyDbg -")

           call get_byte_location
           mov byte_location,eax
           test eax,eax
           jz svthread

           ;1==Debugger 0==Clean ;)
           invoke Writememory,addr null_byte,byte_location,1,MM_RESTORE
           .if (eax!=1)
              ;ooopps
              invoke Error,CTEXT("Error WriteMemory failed")
           .elseif
              invoke  Addtolist, 0, -1, CTEXT("  IsDebugPresent hidden")
           .endif
           invoke ExitThread,TRUE
        .endif

ODBG_Pluginreset endp

; OllyDbg calls this optional function when user wants to terminate OllyDbg.
; All MDI windows created by plugins still exist. Function must return 0 if
; it is safe to terminate. Any non-zero return will stop closing sequence. Do
; not misuse this possibility! Always inform user about the reasons why
; termination is not good and ask for his decision!
ODBG_Pluginclose proc C
        ; For automatical restoring of open windows, mark in .ini file whether
        ; Bookmarks window is still open.
        return  0
ODBG_Pluginclose endp

; Function adds items either to main OllyDbg menu (origin=PM_MAIN) or to popup
; menu in one of standard OllyDbg windows. When plugin wants to add own menu
; items, it gathers menu pattern in data and returns 1, otherwise it must
; return 0. Except for static main menu, plugin must not add inactive items.

; Item indices must range in 0..63. Duplicated indices are explicitly allowed.
ODBG_Pluginmenu proc C uses ebx origin:dword, data:ptr byte, item:dword

        mov     eax, origin
        ; Menu creation is very simple. You just fill in data with menu pattern.
        ; Some examples:
        ; 0 Aaa,2 Bbb|3 Ccc|,,  - linear menu with 3 items, relative IDs 0, 2 and
        ;                         3, separator between second and third item, last
        ;
        ;                         separator and commas are ignored;
        ; #A{0Aaa,B{1Bbb|2Ccc}} - unconditional separator, followed by popup menu
        ;                         A with two elements, second is popup with two
        ;                         elements and separator inbetween.
        .IF eax == PM_MAIN ; Plugin menu in main window
            invoke  lstrcpy, data, CTEXT("0 &Hide,1 &Restore,2 &Option|3 &About|4 &Dumper")
            ;invoke  lstrcpy, data, CTEXT("0 &Hide,1 &Restore|2 &About|3 &Dumper,4 &Initier")
            ; If your plugin is more than trivial, I also recommend to include Help.
            return  1

        .ENDIF
        return  0
ODBG_Pluginmenu endp

; This optional function receives commands from plugin menu in window of type
; origin. Argument action is menu identifier from ODBG_Pluginmenu(). If user

; activates automatically created entry in main menu, action is 0.
ODBG_Pluginaction proc C origin:dword, action:dword, item:dword
        mov     eax, origin
        .IF eax == PM_MAIN
            mov     eax, action
            .IF !eax
                call get_byte_location
                mov byte_location,eax
                .if (eax)
                    ;1==Debugger 0==Clean ;)
                    invoke Writememory,addr null_byte,byte_location,1,MM_RESTORE
                    .if (eax!=1)
                        ;ooopps
                        invoke Error,CTEXT("Error WriteMemory failed")
                    .elseif
                      invoke  Addtolist, 0, -1, CTEXT("  IsDebugPresent hidden")
                    .endif
                .endif
            .ELSEIF eax == 1
                call get_byte_location
                mov byte_location,eax
                .if (eax)
                    ;resore original code
                    invoke Writememory,addr the_byte,byte_location,1,MM_RESTORE
                    .if (eax!=1)
                        ;ooopps
                        invoke Error,CTEXT("Error WriteMemory failed")
                    .elseif
                      invoke  Addtolist, 0, -1, CTEXT("  IsDebugPresent restored")
                    .endif
                .endif
            .ELSEIF eax == 2
                invoke DialogBoxParam, hinst, IDD_OPTION, hwmain, addr DlgOptionProc, NULL
            .ELSEIF eax == 3
                ; Menu item "About", displays plugin info.
                invoke  MessageBox, hwmain, CTEXT("IsDebuggerPresent plugin v1.4",13,10,"(IsDebuggerPresent byte Patcher)",13,10,"Copyright (C) 2oo3 SV",13,10,"MASM32 version"),\
                                            CTEXT("IsDebuggerPresent plugin"),MB_OK or MB_ICONINFORMATION
            .ELSEIF eax == 4
                invoke DialogBoxParam, hinst, IDD_DUMP, hwmain, addr DlgDumpProc, NULL
            .ENDIF
        .ENDIF
        ret
ODBG_Pluginaction endp
get_byte_location proc
    push ebx
    invoke Getcputhreadid
    .if (eax)
        invoke Findthread,eax                  ;retreive thread info
        assume eax:ptr t_thread
        push [eax].reg.base[4*4]               ;base of FS
        pop ebx
        add ebx,30h
        invoke Readmemory,addr dw_buffer,ebx,4,MM_RESTORE
        mov eax,dw_buffer
        add eax,2h
    .endif
    pop ebx
    ret

get_byte_location endp
DlgDumpProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL dump_start:DWORD
LOCAL dump_size:DWORD

    .IF uMsg == WM_INITDIALOG
        mov ofn.lStructSize,SIZEOF ofn
        push hWnd
        pop  ofn.hWndOwner
        push hinst
        pop  ofn.hInstance
        mov  ofn.nMaxFile,MAXSIZE
    .ELSEIF uMsg == WM_CLOSE
        invoke SendMessage, hWnd, WM_COMMAND, IDC_BTCANCEL, 0
    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        mov edx,wParam
        shr edx,16
        .IF dx==BN_CLICKED
            .IF ax==IDC_BTCANCEL
                invoke EndDialog, hWnd, NULL
            .ELSEIF ax==IDC_BTDUMP
                invoke Plugingetvalue,VAL_PROCESSID     ;is something loaded ??
                .if (eax!=0)
                    pushad
                    invoke GetDlgItemText,hWnd,IDC_EDTOFFSET,addr textbuffer,10
                    invoke htodw,addr textbuffer
                    mov dump_start,eax
                    invoke GetDlgItemText,hWnd,IDC_EDTSIZE,addr textbuffer,10
                    invoke htodw,addr textbuffer
                    mov dump_size,eax

                    .if dump_start!=0 && dump_size!=0
                        ;Alloc Mem
                        invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,dump_size
                        mov  hMemory_code,eax
                        invoke GlobalLock,hMemory_code
                        mov  pMemory_code,eax
                        ;Read in Mem
                        invoke Readmemory,pMemory_code,dump_start,dump_size,MM_RESTORE
                        .if (eax==dump_size)
                            push hWnd
                            pop  ofn.hWndOwner
                            mov ofn.Flags,OFN_OVERWRITEPROMPT
                            mov  ofn.lpstrFilter, OFFSET FilterString
                            mov ofn.lpstrDefExt, OFFSET bin_extend
                            mov  ofn.lpstrFile, OFFSET textbuffer
                            mov  ofn.nMaxFile,MAXSIZE
                            mov [textbuffer],0  ;filename buffer a vide
                            invoke GetSaveFileName, ADDR ofn
                            .if eax==TRUE
                                invoke CreateFile,ADDR textbuffer,\
                                       GENERIC_READ or GENERIC_WRITE ,\
                                       FILE_SHARE_READ or FILE_SHARE_WRITE,\
                                       NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,\
                                       NULL
                                mov hFileWrite,eax
                                ;Save mem in file
                                invoke WriteFile,hFileWrite,pMemory_code,dump_size,ADDR SizeWritten,NULL
                                invoke CloseHandle,hFileWrite
                                invoke Flash,CTEXT("File successfully writted !!")
                              .endif
                          .endif
                          ;Free Mem
                        invoke GlobalUnlock,pMemory_code
                        invoke GlobalFree,hMemory_code
                    .endif
                     popad
                .else
                    invoke Error,CTEXT("Error Nothing loaded")
                .endif
            .ENDIF
        .ENDIF
    .ELSE
        mov eax, FALSE
        ret
    .ENDIF
    mov eax, TRUE
    ret
DlgDumpProc endp

DlgOptionProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .IF uMsg == WM_INITDIALOG
        ;read params from ini & update windows
        invoke Pluginreadintfromini,hinst,addr str_idb_Autoload,0
        mov auto_load,eax
        invoke Pluginreadintfromini,hinst,addr str_idb_Sleeptime,1000
        mov sleep_time,eax
        invoke wsprintf,addr textbuffer,offset template,sleep_time
        invoke SetDlgItemText,hWnd,IDC_SLEEPTIME,addr textbuffer
        .if (auto_load!=0)
            invoke GetDlgItem,hWnd,IDC_CHK1
            invoke SendMessage,eax,BM_SETCHECK,BST_CHECKED,0
        .endif
    .ELSEIF uMsg == WM_CLOSE
        invoke SendMessage, hWnd, WM_COMMAND, IDC_BTCANCEL, 0
    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        mov edx,wParam
        shr edx,16
        .IF dx==BN_CLICKED
            .IF ax==IDC_BTN2        ;CANCEL
                invoke EndDialog, hWnd, NULL
            .ELSEIF ax==IDC_BTN1    ;SAVE
                invoke GetDlgItemText,hWnd,IDC_SLEEPTIME,addr textbuffer,10
                invoke atodw,addr textbuffer
                mov sleep_time,eax
                ;save params to ini
                invoke Pluginwriteinttoini,hinst,addr str_idb_Sleeptime,sleep_time
                invoke GetDlgItem,hWnd,IDC_CHK1
                invoke SendMessage,eax,BM_GETSTATE,0,0
                .if eax==BST_CHECKED
                    invoke Pluginwriteinttoini,hinst,addr str_idb_Autoload,1
                .elseif
                    invoke Pluginwriteinttoini,hinst,addr str_idb_Autoload,0
                .endif
               invoke Flash,CTEXT("Option saved !!")
              .ENDIF
        .ENDIF
    .ELSE
        mov eax, FALSE
        ret
    .ENDIF
    mov eax, TRUE
    ret
DlgOptionProc endp

end DllEntryPoint


IsDebug.Def

LIBRARY
EXPORTS
_ODBG_Plugininit
_ODBG_Plugindata
_ODBG_Pluginmenu
_ODBG_Pluginaction
_ODBG_Plugindestroy
_ODBG_Pluginreset
_ODBG_Pluginclose